mirror of
https://github.com/autc04/Retro68.git
synced 2026-04-21 09:17:15 +00:00
Improve ANSI escape codes support
This commit is contained in:
+170
-24
@@ -25,8 +25,10 @@
|
||||
#include "MacUtils.h"
|
||||
#include "Fonts.h"
|
||||
#include "Processes.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
#include <sstream>
|
||||
|
||||
using namespace retro;
|
||||
|
||||
@@ -131,11 +133,13 @@ namespace
|
||||
|
||||
Console::Console()
|
||||
{
|
||||
InitEscapeSequenceMap();
|
||||
}
|
||||
|
||||
Console::Console(GrafPtr port, Rect r)
|
||||
{
|
||||
Init(port, r);
|
||||
InitEscapeSequenceMap();
|
||||
}
|
||||
|
||||
Console::~Console()
|
||||
@@ -270,6 +274,7 @@ void Console::ScrollUp(short n)
|
||||
|
||||
bool Console::ProcessEscSequence(char c)
|
||||
{
|
||||
static std::string argument;
|
||||
switch(sequenceState)
|
||||
{
|
||||
case State::noSequence:
|
||||
@@ -283,31 +288,15 @@ bool Console::ProcessEscSequence(char c)
|
||||
sequenceState=State::noSequence; // Unrecognized sequence
|
||||
break;
|
||||
case State::waitingForControlSequence:
|
||||
sequenceState=State::waitingForM;
|
||||
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:
|
||||
sequenceState=State::noSequence; // Unrecognized sequence
|
||||
if (isalpha(c)) {
|
||||
auto escFunc = escapeSequenceMap.at(c);
|
||||
escFunc(argument);
|
||||
sequenceState=State::noSequence;
|
||||
argument = "";
|
||||
} else {
|
||||
argument = argument + c;
|
||||
}
|
||||
break;
|
||||
case State::waitingForM:
|
||||
if(c=='m')
|
||||
sequenceState=State::noSequence; // Normal end of sequence
|
||||
else
|
||||
sequenceState=State::noSequence; // Unrecognized sequence (but we end it anyway!)
|
||||
break;
|
||||
case State::waitingForOSCStart:
|
||||
if(c=='0')
|
||||
sequenceState=State::waitingForSemicolon;
|
||||
@@ -561,3 +550,160 @@ char Console::WaitNextChar()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Map a letter to a function
|
||||
void Console::InitEscapeSequenceMap()
|
||||
{
|
||||
escapeSequenceMap.insert({'H', [&](std::string args) { Console::SetCursorPosition(args); } });
|
||||
escapeSequenceMap.insert({'J', [&](std::string args) { EraseInDisplay(args); } });
|
||||
escapeSequenceMap.insert({'m', [&](std::string args) { SetDisplayAttributes(args); } });
|
||||
}
|
||||
|
||||
// turns an argument string into numbers
|
||||
// example: "12;13" would return a stack with numbers 13 and 12
|
||||
std::stack<int> parseArguments(std::string str) {
|
||||
std::istringstream iss(str);
|
||||
std::string token;
|
||||
std::stack<int> numberStack;
|
||||
while (getline(iss, token, ';'))
|
||||
{
|
||||
if(token == "")
|
||||
{
|
||||
numberStack.push(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
numberStack.push(atoi(token.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (numberStack.empty())
|
||||
{
|
||||
numberStack.push(1);
|
||||
numberStack.push(1);
|
||||
}
|
||||
|
||||
if (numberStack.size() == 1)
|
||||
{
|
||||
numberStack.push(1);
|
||||
}
|
||||
|
||||
return numberStack;
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code H
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Control Sequence Introducer commands
|
||||
// Name: Cursor Position
|
||||
void Console::SetCursorPosition(std::string args)
|
||||
{
|
||||
// possible formats the arguments can be in:
|
||||
// n -> (1,n)
|
||||
// n;m. -> (m,n)
|
||||
// n;m; -> (m,n)
|
||||
// n; -> (1,n)
|
||||
// ;m -> (m,1)
|
||||
// -> (1,1)
|
||||
|
||||
auto numberStack = parseArguments(args);
|
||||
cursorX = numberStack.top();
|
||||
numberStack.pop();
|
||||
cursorY = numberStack.top();
|
||||
Update();
|
||||
}
|
||||
|
||||
// Bound to ANSI escape code J
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Control Sequence Introducer commands
|
||||
// Name: Erase in Display
|
||||
void Console::EraseInDisplay(std::string args)
|
||||
{
|
||||
int n;
|
||||
if (args == "")
|
||||
n = 0;
|
||||
else
|
||||
n = atoi(args.c_str());
|
||||
|
||||
switch(n) {
|
||||
case 0: // clear from cursor to end of window
|
||||
ClearFromCursorToEndOfWindow();
|
||||
break;
|
||||
|
||||
case 1: // clear from cursor to beginning of the window
|
||||
ClearFromTopOfWindowToCursor();
|
||||
break;
|
||||
|
||||
case 2: // clear entire screen
|
||||
ClearWindow();
|
||||
break;
|
||||
|
||||
case 3: // clear entire screen and delete all lines saved in the scrollback buffer
|
||||
ClearWindow();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets attributes of characters
|
||||
// Note: only a few attributes are currently implemented
|
||||
// Bound to ANSI escape code m
|
||||
// Page: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
// Section: Select Graphic Rendition parameters
|
||||
void Console::SetDisplayAttributes(std::string args)
|
||||
{
|
||||
char c = args.c_str()[0];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Clears the window of all text
|
||||
void Console::ClearWindow()
|
||||
{
|
||||
// Fill the buffer with blank spaces
|
||||
std::fill(chars.begin(), chars.end(), AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin(), onscreen.end(), AttributedChar(' ', currentAttr));
|
||||
|
||||
// Actually erase the window
|
||||
EraseRect(&bounds);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Clears the window of text from the current cursor position to the bottom of the window
|
||||
void Console::ClearFromCursorToEndOfWindow()
|
||||
{
|
||||
int newPosition = cursorY * cols + cursorX;
|
||||
|
||||
// Fill the buffer with blank spaces
|
||||
std::fill(chars.begin() + newPosition, chars.end(), AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin() + newPosition, onscreen.end(), AttributedChar(' ', currentAttr));
|
||||
|
||||
// Actually erase the window
|
||||
EraseRect(&bounds);
|
||||
Update();
|
||||
}
|
||||
|
||||
// Clears the window from the top to the current cursor position
|
||||
void Console::ClearFromTopOfWindowToCursor()
|
||||
{
|
||||
int newPosition = cursorY * cols + cursorX;
|
||||
|
||||
// Fill the buffer with blank spaces
|
||||
std::fill(chars.begin(), chars.begin() + newPosition, AttributedChar(' ', currentAttr));
|
||||
std::fill(onscreen.begin(), onscreen.begin() + newPosition, AttributedChar(' ', currentAttr));
|
||||
|
||||
// Actually erase the window
|
||||
EraseRect(&bounds);
|
||||
Update();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <Quickdraw.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
namespace retro
|
||||
{
|
||||
@@ -105,6 +107,7 @@ namespace retro
|
||||
GrafPtr consolePort = nullptr;
|
||||
Rect bounds;
|
||||
Attributes currentAttr;
|
||||
std::map<char, std::function<void(std::string)>> escapeSequenceMap;
|
||||
|
||||
std::vector<AttributedChar> chars, onscreen;
|
||||
|
||||
@@ -136,6 +139,13 @@ namespace retro
|
||||
void InvalidateCursor();
|
||||
|
||||
virtual char WaitNextChar();
|
||||
void InitEscapeSequenceMap();
|
||||
void SetCursorPosition(std::string);
|
||||
void EraseInDisplay(std::string);
|
||||
void SetDisplayAttributes(std::string);
|
||||
void ClearWindow();
|
||||
void ClearFromCursorToEndOfWindow();
|
||||
void ClearFromTopOfWindowToCursor();
|
||||
|
||||
protected:
|
||||
void Init(GrafPtr port, Rect r);
|
||||
|
||||
Reference in New Issue
Block a user