Skip to content

Commit 932c079

Browse files
authored
Merge pull request #293 from programmingkidx/ANSI-escape-codes-improvements
Improve ANSI escape codes support
2 parents 531a03f + 0892fc8 commit 932c079

File tree

2 files changed

+190
-30
lines changed

2 files changed

+190
-30
lines changed

Console/retro/Console.cc

Lines changed: 178 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
#include "MacUtils.h"
2626
#include "Fonts.h"
2727
#include "Processes.h"
28-
28+
#include <cctype>
2929
#include <algorithm>
30+
#include <stack>
31+
#include <sstream>
3032

3133
using namespace retro;
3234

@@ -131,11 +133,13 @@ namespace
131133

132134
Console::Console()
133135
{
136+
InitEscapeSequenceMap();
134137
}
135138

136139
Console::Console(GrafPtr port, Rect r)
137140
{
138141
Init(port, r);
142+
InitEscapeSequenceMap();
139143
}
140144

141145
Console::~Console()
@@ -283,30 +287,7 @@ bool Console::ProcessEscSequence(char c)
283287
sequenceState=State::noSequence; // Unrecognized sequence
284288
break;
285289
case State::waitingForControlSequence:
286-
sequenceState=State::waitingForM;
287-
switch(c)
288-
{
289-
case '0': // Normal character
290-
currentAttr.reset();
291-
break;
292-
case '1': // Bold
293-
currentAttr.setBold(true);
294-
break;
295-
case '3': // Italic
296-
currentAttr.setItalic(true);
297-
break;
298-
case '4': // Underline
299-
currentAttr.setUnderline(true);
300-
break;
301-
default:
302-
sequenceState=State::noSequence; // Unrecognized sequence
303-
}
304-
break;
305-
case State::waitingForM:
306-
if(c=='m')
307-
sequenceState=State::noSequence; // Normal end of sequence
308-
else
309-
sequenceState=State::noSequence; // Unrecognized sequence (but we end it anyway!)
290+
HandleControlSequence(c);
310291
break;
311292
case State::waitingForOSCStart:
312293
if(c=='0')
@@ -318,21 +299,21 @@ bool Console::ProcessEscSequence(char c)
318299
if(c==';')
319300
{
320301
sequenceState=State::inWindowName;
321-
windowName="";
302+
argument = "";
322303
}
323304
else
324305
sequenceState=State::noSequence; // Normal end of sequence
325306
break;
326307
case State::inWindowName:
327308
if(c==BEL)
328309
{
329-
setWindowName(std::move(windowName));
310+
setWindowName(std::move(argument));
330311
sequenceState=State::noSequence; // Normal end of sequence
331312
}
332313
else
333314
{
334-
if(windowName.size() < MAX_LEN) // Ignore subsequent characters
335-
windowName+=c;
315+
if(argument.size() < MAX_LEN) // Ignore subsequent characters
316+
argument+=c;
336317
}
337318
break;
338319
default:
@@ -561,3 +542,171 @@ char Console::WaitNextChar()
561542
{
562543
return 0;
563544
}
545+
546+
// Map a letter to a function
547+
void Console::InitEscapeSequenceMap()
548+
{
549+
escapeSequenceMap.insert({'H', [&](std::string args) { Console::SetCursorPosition(args); } });
550+
escapeSequenceMap.insert({'J', [&](std::string args) { EraseInDisplay(args); } });
551+
escapeSequenceMap.insert({'m', [&](std::string args) { SetDisplayAttributes(args); } });
552+
}
553+
554+
// turns an argument string into numbers
555+
// example: "12;13" would return a vector with numbers 12 and 13
556+
static std::vector<int> parseArguments(std::string str) {
557+
std::istringstream iss(str);
558+
std::string token;
559+
std::vector<int> numberVector;
560+
while (getline(iss, token, ';'))
561+
{
562+
if(token == "")
563+
{
564+
numberVector.push_back(1);
565+
}
566+
else
567+
{
568+
numberVector.push_back(atoi(token.c_str()));
569+
}
570+
}
571+
572+
while (numberVector.size() < 2)
573+
numberVector.push_back(1);
574+
575+
return numberVector;
576+
}
577+
578+
// Bound to ANSI escape code H
579+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
580+
// Section: Control Sequence Introducer commands
581+
// Name: Cursor Position
582+
void Console::SetCursorPosition(std::string args)
583+
{
584+
// possible formats the arguments can be in:
585+
// n -> (1,n)
586+
// n;m. -> (m,n)
587+
// n;m; -> (m,n)
588+
// n; -> (1,n)
589+
// ;m -> (m,1)
590+
// -> (1,1)
591+
592+
auto numberVector = parseArguments(args);
593+
cursorX = numberVector.at(1);
594+
cursorY = numberVector.at(0);
595+
Update();
596+
}
597+
598+
// Bound to ANSI escape code J
599+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
600+
// Section: Control Sequence Introducer commands
601+
// Name: Erase in Display
602+
void Console::EraseInDisplay(std::string args)
603+
{
604+
int n;
605+
if (args == "")
606+
n = 0;
607+
else
608+
n = atoi(args.c_str());
609+
610+
switch(n) {
611+
case 0: // clear from cursor to end of window
612+
ClearFromCursorToEndOfWindow();
613+
break;
614+
615+
case 1: // clear from cursor to beginning of the window
616+
ClearFromTopOfWindowToCursor();
617+
break;
618+
619+
case 2: // clear entire screen
620+
ClearWindow();
621+
break;
622+
623+
case 3: // clear entire screen and delete all lines saved in the scrollback buffer
624+
ClearWindow();
625+
break;
626+
}
627+
}
628+
629+
// Sets attributes of characters
630+
// Note: only a few attributes are currently implemented
631+
// Bound to ANSI escape code m
632+
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
633+
// Section: Select Graphic Rendition parameters
634+
void Console::SetDisplayAttributes(std::string args)
635+
{
636+
char c = args.c_str()[0];
637+
switch(c)
638+
{
639+
case '0': // Normal character
640+
currentAttr.reset();
641+
break;
642+
case '1': // Bold
643+
currentAttr.setBold(true);
644+
break;
645+
case '3': // Italic
646+
currentAttr.setItalic(true);
647+
break;
648+
case '4': // Underline
649+
currentAttr.setUnderline(true);
650+
break;
651+
}
652+
}
653+
654+
// Clears the window of all text
655+
void Console::ClearWindow()
656+
{
657+
// Fill the buffer with blank spaces
658+
std::fill(chars.begin(), chars.end(), AttributedChar(' ', currentAttr));
659+
std::fill(onscreen.begin(), onscreen.end(), AttributedChar(' ', currentAttr));
660+
661+
// Erase the window
662+
EraseRect(&bounds);
663+
Update();
664+
Draw(bounds);
665+
}
666+
667+
// Clears the window of text from the current cursor position to the bottom of the window
668+
void Console::ClearFromCursorToEndOfWindow()
669+
{
670+
int newPosition = cursorY * cols + cursorX;
671+
672+
// Fill the buffer with blank spaces
673+
std::fill(chars.begin() + newPosition, chars.end(), AttributedChar(' ', currentAttr));
674+
std::fill(onscreen.begin() + newPosition, onscreen.end(), AttributedChar(' ', currentAttr));
675+
676+
// Erase the window
677+
EraseRect(&bounds);
678+
Update();
679+
Draw(bounds);
680+
}
681+
682+
// Clears the window from the top to the current cursor position
683+
void Console::ClearFromTopOfWindowToCursor()
684+
{
685+
int newPosition = cursorY * cols + cursorX;
686+
687+
// Fill the buffer with blank spaces
688+
std::fill(chars.begin(), chars.begin() + newPosition, AttributedChar(' ', currentAttr));
689+
std::fill(onscreen.begin(), onscreen.begin() + newPosition, AttributedChar(' ', currentAttr));
690+
691+
// Erase the window
692+
EraseRect(&bounds);
693+
Update();
694+
Draw(bounds);
695+
}
696+
697+
// handles the waitingForControlSequence state
698+
void Console::HandleControlSequence(char c)
699+
{
700+
if (isalpha(c))
701+
{
702+
auto escFunc = escapeSequenceMap.at(c);
703+
escFunc(argument);
704+
sequenceState=State::noSequence;
705+
argument = "";
706+
}
707+
708+
else
709+
{
710+
argument = argument + c;
711+
}
712+
}

Console/retro/Console.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include <Quickdraw.h>
2727
#include <vector>
2828
#include <string>
29+
#include <map>
30+
#include <functional>
2931

3032
namespace retro
3133
{
@@ -101,10 +103,11 @@ namespace retro
101103
private:
102104

103105
State sequenceState;
104-
std::string windowName;
106+
std::string argument;
105107
GrafPtr consolePort = nullptr;
106108
Rect bounds;
107109
Attributes currentAttr;
110+
std::map<char, std::function<void(std::string)>> escapeSequenceMap;
108111

109112
std::vector<AttributedChar> chars, onscreen;
110113

@@ -136,6 +139,14 @@ namespace retro
136139
void InvalidateCursor();
137140

138141
virtual char WaitNextChar();
142+
void InitEscapeSequenceMap();
143+
void SetCursorPosition(std::string);
144+
void EraseInDisplay(std::string);
145+
void SetDisplayAttributes(std::string);
146+
void ClearWindow();
147+
void ClearFromCursorToEndOfWindow();
148+
void ClearFromTopOfWindowToCursor();
149+
void HandleControlSequence(char);
139150

140151
protected:
141152
void Init(GrafPtr port, Rect r);

0 commit comments

Comments
 (0)