Skip to content

Commit 8b51c02

Browse files
authored
Merge pull request #294 from programmingkidx/Add-more-ANSI-escape-codes
Add support for more ANSI escape codes
2 parents 932c079 + 746807d commit 8b51c02

File tree

2 files changed

+318
-6
lines changed

2 files changed

+318
-6
lines changed

Console/retro/Console.cc

Lines changed: 297 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ void Console::Update()
404404
Rect r = CellRect(cursorX, cursorY);
405405
if(cursorDrawn)
406406
DrawCell(cursorX, cursorY, true);
407-
else
407+
else if(cursorRequestedHidden == false)
408408
InvertRect(&r);
409409
cursorDrawn = !cursorDrawn;
410410
}
@@ -546,9 +546,21 @@ char Console::WaitNextChar()
546546
// Map a letter to a function
547547
void Console::InitEscapeSequenceMap()
548548
{
549-
escapeSequenceMap.insert({'H', [&](std::string args) { Console::SetCursorPosition(args); } });
549+
escapeSequenceMap.insert({'A', [&](std::string args) { MoveCursorUp(args); } });
550+
escapeSequenceMap.insert({'B', [&](std::string args) { MoveCursorDown(args); } });
551+
escapeSequenceMap.insert({'C', [&](std::string args) { MoveCursorForward(args); } });
552+
escapeSequenceMap.insert({'D', [&](std::string args) { MoveCursorBack(args); } });
553+
escapeSequenceMap.insert({'E', [&](std::string args) { MoveCursorNextLine(args); } });
554+
escapeSequenceMap.insert({'F', [&](std::string args) { MoveCursorPreviousLine(args); } });
555+
escapeSequenceMap.insert({'G', [&](std::string args) { MoveCursorHorizonalAbsolute(args); } });
556+
escapeSequenceMap.insert({'H', [&](std::string args) { SetCursorPosition(args); } });
550557
escapeSequenceMap.insert({'J', [&](std::string args) { EraseInDisplay(args); } });
558+
escapeSequenceMap.insert({'K', [&](std::string args) { EraseInLine(args); } });
559+
escapeSequenceMap.insert({'h', [&](std::string args) { ShowCursor(args); } });
560+
escapeSequenceMap.insert({'l', [&](std::string args) { HideCursor(args); } });
551561
escapeSequenceMap.insert({'m', [&](std::string args) { SetDisplayAttributes(args); } });
562+
escapeSequenceMap.insert({'s', [&](std::string args) { SaveCursorPosition(args); } });
563+
escapeSequenceMap.insert({'u', [&](std::string args) { RestoreCursorPosition(args); } });
552564
}
553565

554566
// turns an argument string into numbers
@@ -590,8 +602,8 @@ void Console::SetCursorPosition(std::string args)
590602
// -> (1,1)
591603

592604
auto numberVector = parseArguments(args);
593-
cursorX = numberVector.at(1);
594-
cursorY = numberVector.at(0);
605+
SetCursorX(numberVector.at(1));
606+
SetCursorY(numberVector.at(0));
595607
Update();
596608
}
597609

@@ -667,7 +679,7 @@ void Console::ClearWindow()
667679
// Clears the window of text from the current cursor position to the bottom of the window
668680
void Console::ClearFromCursorToEndOfWindow()
669681
{
670-
int newPosition = cursorY * cols + cursorX;
682+
int newPosition = GetCursorY() * cols + GetCursorX() - 1;
671683

672684
// Fill the buffer with blank spaces
673685
std::fill(chars.begin() + newPosition, chars.end(), AttributedChar(' ', currentAttr));
@@ -682,7 +694,7 @@ void Console::ClearFromCursorToEndOfWindow()
682694
// Clears the window from the top to the current cursor position
683695
void Console::ClearFromTopOfWindowToCursor()
684696
{
685-
int newPosition = cursorY * cols + cursorX;
697+
int newPosition = GetCursorY() * cols + GetCursorX();
686698

687699
// Fill the buffer with blank spaces
688700
std::fill(chars.begin(), chars.begin() + newPosition, AttributedChar(' ', currentAttr));
@@ -710,3 +722,282 @@ void Console::HandleControlSequence(char c)
710722
argument = argument + c;
711723
}
712724
}
725+
726+
// Bound to ANSI escape code A
727+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
728+
// Section: Some ANSI control sequences
729+
// Name: Cursor Up
730+
void Console::MoveCursorUp(std::string args)
731+
{
732+
auto numberVector = parseArguments(args);
733+
int lines;
734+
if (numberVector.size() == 0)
735+
{
736+
lines = 1;
737+
}
738+
else
739+
{
740+
lines = numberVector.at(0);
741+
}
742+
SetCursorY(GetCursorY() - lines);
743+
Update();
744+
}
745+
746+
// Bound to ANSI escape code B
747+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
748+
// Section: Some ANSI control sequences
749+
// Name: Cursor Down
750+
void Console::MoveCursorDown(std::string args)
751+
{
752+
auto numberVector = parseArguments(args);
753+
int lines;
754+
if (numberVector.size() == 0)
755+
{
756+
lines = 1;
757+
}
758+
else
759+
{
760+
lines = numberVector.at(0);
761+
}
762+
SetCursorY(GetCursorY() + lines);
763+
Update();
764+
}
765+
766+
// Bound to ANSI escape code C
767+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
768+
// Section: Some ANSI control sequences
769+
// Name: Cursor Forward
770+
void Console::MoveCursorForward(std::string args)
771+
{
772+
auto numberVector = parseArguments(args);
773+
int columns;
774+
if (numberVector.size() == 0)
775+
{
776+
columns = 1;
777+
}
778+
else
779+
{
780+
columns = numberVector.at(0);
781+
}
782+
SetCursorX(GetCursorX() + columns);
783+
Update();
784+
}
785+
786+
// Bound to ANSI escape code D
787+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
788+
// Section: Some ANSI control sequences
789+
// Name: Cursor Back
790+
void Console::MoveCursorBack(std::string args)
791+
{
792+
auto numberVector = parseArguments(args);
793+
int columns;
794+
if (numberVector.size() == 0)
795+
{
796+
columns = 1;
797+
}
798+
else
799+
{
800+
columns = numberVector.at(0);
801+
}
802+
SetCursorX(GetCursorX() - columns);
803+
Update();
804+
}
805+
806+
// Bound to ANSI escape code E
807+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
808+
// Section: Some ANSI control sequences
809+
// Name: Cursor Next Line
810+
void Console::MoveCursorNextLine(std::string args)
811+
{
812+
auto numberVector = parseArguments(args);
813+
int lines;
814+
if (numberVector.size() == 0)
815+
{
816+
lines = 1;
817+
}
818+
else
819+
{
820+
lines = numberVector.at(0);
821+
}
822+
SetCursorX(1);
823+
SetCursorY(GetCursorY() + lines);
824+
Update();
825+
}
826+
827+
// Bound to ANSI escape code F
828+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
829+
// Section: Some ANSI control sequences
830+
// Name: Cursor Previous Line
831+
void Console::MoveCursorPreviousLine(std::string args)
832+
{
833+
auto numberVector = parseArguments(args);
834+
int lines;
835+
if (numberVector.size() == 0)
836+
{
837+
lines = 1;
838+
}
839+
else
840+
{
841+
lines = numberVector.at(0);
842+
}
843+
SetCursorX(1);
844+
SetCursorY(GetCursorY() - lines);
845+
Update();
846+
}
847+
848+
// Bound to ANSI escape code G
849+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
850+
// Section: Some ANSI control sequences
851+
// Name: Cursor Horizontal Absolute
852+
void Console::MoveCursorHorizonalAbsolute(std::string args)
853+
{
854+
auto numberVector = parseArguments(args);
855+
auto newPosition = numberVector.at(0);
856+
SetCursorX(newPosition);
857+
Update();
858+
}
859+
860+
// Bound to ANSI escape code K
861+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
862+
// Section: Some ANSI control sequences
863+
// Name: Erase in Line
864+
void Console::EraseInLine(std::string args)
865+
{
866+
auto numberVector = parseArguments(args);
867+
int argument = numberVector.at(0);
868+
switch(argument)
869+
{
870+
case 0:
871+
ClearFromCursorToEndOfLine();
872+
break;
873+
case 1:
874+
ClearFromBeginningOfLineToCursor();
875+
break;
876+
case 2:
877+
ClearEntireLine();
878+
break;
879+
}
880+
}
881+
882+
// Erases from the current cursor position to the end of the line
883+
void Console::ClearFromCursorToEndOfLine()
884+
{
885+
int currentPosition = (GetCursorY() - 1) * cols + GetCursorX() - 1;
886+
int endOfLinePosition = GetCursorY() * cols;
887+
888+
// Fill part of the buffer with blank spaces
889+
std::fill(chars.begin() + currentPosition, chars.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
890+
std::fill(onscreen.begin() + currentPosition, onscreen.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
891+
892+
Update();
893+
894+
// Erase only on the line the cursor is on
895+
Rect rect;
896+
rect = CellRect(cursorX, cursorY);
897+
rect.right = cols * cellSizeX;
898+
EraseRect(&rect);
899+
}
900+
901+
// Erases from the beginning of the line to the cursor's position
902+
void Console::ClearFromBeginningOfLineToCursor()
903+
{
904+
int currentPosition = (GetCursorY() - 1) * cols + GetCursorX();
905+
int beginningOfLinePosition = (GetCursorY() - 1) * cols;
906+
907+
// Fill part of the buffer with blank spaces
908+
std::fill(chars.begin() + beginningOfLinePosition, chars.begin() + currentPosition, AttributedChar(' ', currentAttr));
909+
std::fill(onscreen.begin() + beginningOfLinePosition, onscreen.begin() + currentPosition, AttributedChar(' ', currentAttr));
910+
911+
Update();
912+
913+
// Erase only on the line the cursor is on
914+
Rect rect;
915+
rect = CellRect(0, cursorY);
916+
rect.right = GetCursorX() * cellSizeX;
917+
EraseRect(&rect);
918+
}
919+
920+
// Erases the entire line the cursor is on
921+
void Console::ClearEntireLine()
922+
{
923+
int beginningOfLinePosition = (GetCursorY() - 1) * cols;
924+
int endOfLinePosition = GetCursorY() * cols;
925+
926+
// Fill part of the buffer with blank spaces
927+
std::fill(chars.begin() + beginningOfLinePosition, chars.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
928+
std::fill(onscreen.begin() + beginningOfLinePosition, onscreen.begin() + endOfLinePosition, AttributedChar(' ', currentAttr));
929+
930+
Update();
931+
932+
// Erase only the line the cursor is on
933+
Rect rect;
934+
rect = CellRect(0, cursorY);
935+
rect.right = cols * cellSizeX;
936+
EraseRect(&rect);
937+
}
938+
939+
// Bound to ANSI escape code h
940+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
941+
// Section: Some popular private sequences
942+
// Description: Sets a variable to indicate the cursor should be shown
943+
void Console::ShowCursor(std::string args)
944+
{
945+
cursorRequestedHidden = false;
946+
}
947+
948+
// Bound to ANSI escape code l
949+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
950+
// Section: Some popular private sequences
951+
// Description: Sets a variable to indicate the cursor should be hidden
952+
void Console::HideCursor(std::string args)
953+
{
954+
cursorRequestedHidden = true;
955+
}
956+
957+
// Bound to ANSI escape code s
958+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
959+
// Section: Some popular private sequences
960+
// Description: Saves the current cursor position
961+
void Console::SaveCursorPosition(std::string args)
962+
{
963+
savedCursorX = cursorX;
964+
savedCursorY = cursorY;
965+
}
966+
967+
// Bound to ANSI escape code u
968+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
969+
// Section: Some popular private sequences
970+
// Description: Restores the cursor's position to the saved value
971+
void Console::RestoreCursorPosition(std::string args)
972+
{
973+
cursorX = savedCursorX;
974+
cursorY = savedCursorY;
975+
}
976+
977+
/*
978+
These setter and getter functions fix a problem where ANSI escape codes expect
979+
an origin of (1,1) while the Console class expects an origin of (0,0).
980+
Only functions that work with ANSI escape codes should use these functions.
981+
*/
982+
983+
void Console::SetCursorX(int newX)
984+
{
985+
cursorX = newX - 1;
986+
cursorX = cursorX < 0 ? 0 : cursorX; // Terminal.app does this so we will too
987+
}
988+
989+
int Console::GetCursorX()
990+
{
991+
return cursorX + 1;
992+
}
993+
994+
void Console::SetCursorY(int newY)
995+
{
996+
cursorY = newY - 1;
997+
cursorY = cursorY < 0 ? 0 : cursorY; // Terminal.app does this so we will too
998+
}
999+
1000+
int Console::GetCursorY()
1001+
{
1002+
return cursorY + 1;
1003+
}

Console/retro/Console.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,14 @@ namespace retro
117117
short rows = 0, cols = 0;
118118

119119
short cursorX, cursorY;
120+
short savedCursorX, savedCursorY;
120121

121122
Rect dirtyRect = {};
122123

123124
long blinkTicks = 0;
124125
bool cursorDrawn = false;
125126
bool cursorVisible = true;
127+
bool cursorRequestedHidden = false;
126128
bool eof = false;
127129

128130
void PutCharNoUpdate(char c);
@@ -147,6 +149,25 @@ namespace retro
147149
void ClearFromCursorToEndOfWindow();
148150
void ClearFromTopOfWindowToCursor();
149151
void HandleControlSequence(char);
152+
void MoveCursorUp(std::string args);
153+
void MoveCursorDown(std::string args);
154+
void MoveCursorForward(std::string args);
155+
void MoveCursorBack(std::string args);
156+
void MoveCursorNextLine(std::string args);
157+
void MoveCursorPreviousLine(std::string args);
158+
void MoveCursorHorizonalAbsolute(std::string args);
159+
void EraseInLine(std::string args);
160+
void ClearFromCursorToEndOfLine();
161+
void ClearFromBeginningOfLineToCursor();
162+
void ClearEntireLine();
163+
void ShowCursor(std::string args);
164+
void HideCursor(std::string args);
165+
void SaveCursorPosition(std::string args);
166+
void RestoreCursorPosition(std::string args);
167+
void SetCursorX(int newX);
168+
int GetCursorX();
169+
void SetCursorY(int newY);
170+
int GetCursorY();
150171

151172
protected:
152173
void Init(GrafPtr port, Rect r);

0 commit comments

Comments
 (0)