Skip to content

Commit 4c2d8d1

Browse files
Add support for more ANSI escape codes
1 parent 932c079 commit 4c2d8d1

File tree

2 files changed

+278
-6
lines changed

2 files changed

+278
-6
lines changed

Console/retro/Console.cc

Lines changed: 260 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,8 +546,18 @@ 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); } });
552562
}
553563

@@ -590,8 +600,8 @@ void Console::SetCursorPosition(std::string args)
590600
// -> (1,1)
591601

592602
auto numberVector = parseArguments(args);
593-
cursorX = numberVector.at(1);
594-
cursorY = numberVector.at(0);
603+
SetCursorX(numberVector.at(1));
604+
SetCursorY(numberVector.at(0));
595605
Update();
596606
}
597607

@@ -667,7 +677,7 @@ void Console::ClearWindow()
667677
// Clears the window of text from the current cursor position to the bottom of the window
668678
void Console::ClearFromCursorToEndOfWindow()
669679
{
670-
int newPosition = cursorY * cols + cursorX;
680+
int newPosition = GetCursorY() * cols + GetCursorX() - 1;
671681

672682
// Fill the buffer with blank spaces
673683
std::fill(chars.begin() + newPosition, chars.end(), AttributedChar(' ', currentAttr));
@@ -682,7 +692,7 @@ void Console::ClearFromCursorToEndOfWindow()
682692
// Clears the window from the top to the current cursor position
683693
void Console::ClearFromTopOfWindowToCursor()
684694
{
685-
int newPosition = cursorY * cols + cursorX;
695+
int newPosition = GetCursorY() * cols + GetCursorX();
686696

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

Console/retro/Console.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ namespace retro
123123
long blinkTicks = 0;
124124
bool cursorDrawn = false;
125125
bool cursorVisible = true;
126+
bool cursorRequestedHidden = false;
126127
bool eof = false;
127128

128129
void PutCharNoUpdate(char c);
@@ -147,6 +148,23 @@ namespace retro
147148
void ClearFromCursorToEndOfWindow();
148149
void ClearFromTopOfWindowToCursor();
149150
void HandleControlSequence(char);
151+
void MoveCursorUp(std::string args);
152+
void MoveCursorDown(std::string args);
153+
void MoveCursorForward(std::string args);
154+
void MoveCursorBack(std::string args);
155+
void MoveCursorNextLine(std::string args);
156+
void MoveCursorPreviousLine(std::string args);
157+
void MoveCursorHorizonalAbsolute(std::string args);
158+
void EraseInLine(std::string args);
159+
void ClearFromCursorToEndOfLine();
160+
void ClearFromBeginningOfLineToCursor();
161+
void ClearEntireLine();
162+
void ShowCursor(std::string args);
163+
void HideCursor(std::string args);
164+
void SetCursorX(int newX);
165+
int GetCursorX();
166+
void SetCursorY(int newY);
167+
int GetCursorY();
150168

151169
protected:
152170
void Init(GrafPtr port, Rect r);

0 commit comments

Comments
 (0)