ifengine/ansiterm.c
2018-09-18 19:59:40 -05:00

581 lines
12 KiB
C

#include <string.h>
#include <stdarg.h>
#include "joey.h"
#include "ansiterm.h"
segment "ansiterm";
// http://ansi-bbs.org/ansi-bbs-core-server.html
typedef struct {
byte x;
byte y;
} PositionT;
static byte _columns = 40;
static byte _rows = 25;
static byte _xoff = 0;
static byte _yoff = 0;
static bool _escFound = false;
static bool _inEscape = false;
static bool _bold = false;
static bool _blink = false;
static bool _reverse = false;
static bool _destructiveBS = false;
static byte _foreground = 7;
static byte _background = 0;
static byte _fColor = 7;
static byte _bColor = 0;
static byte _number = 0;
static byte _parameters[5];
static byte _parameterCount = 0;
static byte _hiddenLines = 0;
static jlStaT *_ansiFont = NULL;
static PositionT _cursorSave;
static PositionT _cursor;
static PositionT *_screenBuffer = NULL;
void termDebug(const char *what, ...);
bool termParseANSI(char c);
void termRenderCharacterAtCursor(byte c);
void termResetSequence(void);
void termScrollUp(void);
void termUpdateColors(void);
void termClearScreen(void) {
int i = 0;
int x;
int y;
for (y=0; y<_rows; y++) {
for (x=0; x<_columns; x++) {
_screenBuffer[i].x = 0;
_screenBuffer[i].y = 0;
}
}
}
__attribute__((__format__ (__printf__, 1, 0)))
void termDebug(const char *what, ...) {
char msg[1024];
va_list va;
va_start(va, what);
vsprintf(msg, what, va);
va_end(va);
printf("%s\n", msg);
}
void termDestruciveBackspace(bool dbs) {
_destructiveBS = dbs;
}
void termGetCursor(byte *x, byte *y) {
*x = _cursor.x;
*y = _cursor.y;
}
void termHideTopLines(byte count) {
_hiddenLines = count;
}
void termMoveCursor(byte x, byte y) {
if (x < 1) {
_cursor.x = 1;
termDebug("Attempt to position cursor too far left: %d", x);
} else {
if (x > _columns) {
_cursor.x = _columns;
termDebug("Attempt to position cursor too far right: %d", x);
} else {
_cursor.x = x;
}
}
if (y < 1) {
_cursor.y = 1;
termDebug("Attempt to position cursor too far up: %d", y);
} else {
if (y > _rows) {
_cursor.y = _rows;
termDebug("Attempt to position cursor too far down: %d", y);
} else {
_cursor.y = y;
}
}
}
bool termParseANSI(char c) {
bool x;
bool y;
bool oldX;
bool cx;
int p;
bool updateDisplay = false;
PositionT cursor = _cursor;
// Find leading ESC character.
if ((c == (char)27) && !_escFound && !_inEscape) {
_escFound = true;
return updateDisplay;
}
// Find [ character.
if ((c == '[') && _escFound && !_inEscape) {
_inEscape = true;
return updateDisplay;
}
// We don't do non-'ESC[' sequences.
if (_escFound && !_inEscape) {
termDebug("Unexpected Character: %d", (int)c);
termResetSequence();
}
if (_inEscape) {
switch (c) {
// All we know is that a Xenomorph may be involved.
case '[':
case (char)27:
termDebug("BUG! Escape found inside sequence.");
break;
// End of a parameter.
case ';':
_parameters[_parameterCount++] = _number;
_number = 0;
break;
// Cursor up.
case 'A':
y = _number;
termDebug("Moving cursor up %d", y);
termMoveCursor(cursor.x, cursor.y - y);
termResetSequence();
break;
// Cursor down.
case 'B':
y = _number;
termDebug("Moving cursor down %d", y);
termMoveCursor(cursor.x, cursor.y + y);
termResetSequence();
break;
// Cursor forward.
case 'C':
x = _number;
termDebug("Moving cursor right %d", x);
termMoveCursor(cursor.x + x, cursor.y);
termResetSequence();
break;
// Cursor backward.
case 'D':
x = _number;
termDebug("Moving cursor left %d", x);
termMoveCursor(cursor.x - x, cursor.y);
termResetSequence();
break;
// Cursor line down.
case 'E':
y = _number;
termDebug("Moving cursor down line %d", y);
termMoveCursor(1, cursor.y + y);
//***TODO*** This should allow scrolling
termResetSequence();
break;
// Cursor line up.
case 'F':
y = _number;
termDebug("Moving cursor up line %d", y);
termMoveCursor(1, cursor.y - y);
termResetSequence();
break;
// Cursor horizontal absolute.
case 'G':
x = _number;
termDebug("Moving cursor horizontally to %d", x);
termMoveCursor(x, cursor.y);
termResetSequence();
break;
// Move cursor.
case 'H':
case 'f':
switch (_parameterCount) {
case 0:
// Absolute position, Y. Kinda. Moves X to 1.
y = _number;
termDebug("Moving cursor to 1x%d", y);
termMoveCursor(1, y);
break;
case 1:
// Absolute position.
x = _number;
y = _parameters[0];
termDebug("Moving cursor to %dx%d", x, y);
termMoveCursor(x, y);
break;
default:
termDebug("Unknown Cursor Move");
break;
}
termResetSequence();
break;
// Clear display.
case 'J':
if ((_parameterCount == 0) && (_number == 2)) {
termDebug("Clear display");
termClearScreen();
updateDisplay = true;
termMoveCursor(1, 1);
} else {
termDebug("Unimplemented Screen Clear");
}
termResetSequence();
break;
// Clear from cursor to end of line.
case 'K':
x = _number;
if (x == 0) {
termDebug("Clear to end of line");
oldX = cursor.x;
for (cx=cursor.x; cx<=_columns; cx++) {
termMoveCursor(cx, cursor.y);
termRenderCharacterAtCursor(' ');
}
termMoveCursor(oldX, cursor.y);
} else {
termDebug("Unimplemented Line Clear");
}
termResetSequence();
break;
// Insert lines.
case 'L':
//***TODO***
termDebug("Unimplemented Insert Line");
termResetSequence();
break;
// Delete lines.
case 'M':
//***TODO***
termDebug("Unimplemented Delete Line");
termResetSequence();
break;
// Delete characters.
case 'P':
//***TODO***
termDebug("Unimplemented Delete Character");
termResetSequence();
break;
// Scroll Up.
case 'S':
//***TODO***
termDebug("Unimplemented Scroll Up");
termResetSequence();
break;
// Scroll Down.
case 'T':
//***TODO***
termDebug("Unimplemented Scroll Down");
termResetSequence();
break;
// Clear Screen with Normal Attribute.
case 'U':
x = _bColor;
_bColor = 0;
termUpdateColors();
termClearScreen();
updateDisplay = true;
_bColor = x;
termUpdateColors();
termMoveCursor(1, 1);
termResetSequence();
break;
// Back-TAB.
case 'Z':
//***TODO***
termDebug("Unimplemented Back TAB");
termResetSequence();
break;
// Set attributes.
case 'm':
_parameters[_parameterCount++] = _number;
for (p=0; p<_parameterCount; p++) {
termDebug("Set attribute %d", _parameters[p]);
x = _parameters[p];
switch (x) {
case 0:
_bold = false;
_blink = false;
_reverse = false;
_fColor = 7;
_bColor = 0;
termUpdateColors();
break;
case 1:
_bold = true;
termUpdateColors();
break;
case 5:
case 6:
_blink = true;
break;
case 7:
if (!_reverse) {
x = _fColor;
_fColor = _bColor;
_bColor = x;
termUpdateColors();
}
_reverse = true;
break;
case 21:
case 22:
_bold = false;
termUpdateColors();
break;
case 25:
_blink = false;
break;
case 27:
if (_reverse) {
x = _fColor;
_fColor = _bColor;
_bColor = x;
termUpdateColors();
}
_reverse = false;
break;
default:
if ((x > 29) && (x < 38)) {
_fColor = x - 30;
termUpdateColors();
} else {
if ((x > 39) && (x < 48)) {
_bColor = x - 40;
termUpdateColors();
} else {
termDebug("Unknown attribute: %d", (int)c);
}
}
break;
}
}
termResetSequence();
break;
// Define scroll region.
case 'r':
//***TODO***
termDebug("Unimplemented Scroll Region");
termResetSequence();
break;
// Cursor save.
case 's':
termDebug("Saving cursor");
_cursorSave = cursor;
termResetSequence();
break;
// Cursor restore.
case 'u':
termDebug("Restoring cursor");
termMoveCursor(_cursorSave.x, _cursorSave.y);
termResetSequence();
break;
// Something else.
default:
// Number?
if ((c >= '0') && (c <= '9')) {
_number = (byte)(_number * 10 + (c - '0'));
} else {
termDebug("Unknown sequence: %d", (int)c);
termResetSequence();
}
break;
}
} else {
switch (c) {
case (char)7:
// Beep!
termDebug("Unimplemented beep");
break;
case (char)8:
case (char)127:
// Backspace
if (cursor.x > 1) {
cursor.x--;
} else {
if (cursor.y > 1) {
termDebug("Cursor wrapped off left");
cursor.y--;
cursor.x = _columns;
}
}
termMoveCursor(cursor.x, cursor.y);
if (_destructiveBS) {
termRenderCharacterAtCursor(' ');
updateDisplay = true;
}
break;
case (char)9:
//***TODO*** TAB
termDebug("Unimplemented tab");
break;
case (char)10:
cursor.y++;
if (cursor.y > _rows) {
cursor.y--;
termScrollUp();
updateDisplay = true;
}
termMoveCursor(cursor.x, cursor.y);
break;
case (char)12:
x = _bColor;
_bColor = 0;
termUpdateColors();
termClearScreen();
updateDisplay = true;
_bColor = x;
termUpdateColors();
termMoveCursor(1, 1);
break;
case (char)13:
termMoveCursor(1, cursor.y);
break;
default:
updateDisplay = true;
termRenderCharacterAtCursor((byte)c);
if (_blink) {
//***TODO*** Not handled
}
cursor.x++;
if (cursor.x > _columns) {
termDebug("Cursor wrapped off right");
cursor.x = 1;
cursor.y++;
if (cursor.y > _rows) {
termDebug("Cursor went off bottom");
cursor.y--;
termScrollUp();
}
}
termMoveCursor(cursor.x, cursor.y);
break;
}
}
return updateDisplay;
}
void termPrint(char *string) {
int x;
int l = (int)strlen(string);
for (x=0; x<l; x++) {
termPrintChar(string[x]);
}
}
void termPrintChar(char c) {
if (termParseANSI(c)) {
jlDisplayPresent();
}
}
void termRenderCharacterAtCursor(byte c) {
byte r = (_reverse ? 7 : 0);
byte cx = c % 40;
byte cy = (c / 40) + r;
int i = ((_cursor.y - 1) * _columns) + (_cursor.x - 1);
_screenBuffer[i].x = cx;
_screenBuffer[i].y = cy;
if (_cursor.y > _hiddenLines) {
jlDrawBlit8x8(_ansiFont, cx, cy, _cursor.x - 1 + _xoff, _cursor.y - 1 + _yoff);
}
}
void termRepaint(void) {
int i = _hiddenLines * _columns;
int x;
int y;
for (y=_hiddenLines; y<_rows; y++) {
for (x=0; x<_columns; x++) {
jlDrawBlit8x8(_ansiFont, _screenBuffer[i].x, _screenBuffer[i].y, x + _xoff, y + _yoff);
i++;
}
}
}
void termResetSequence(void) {
_number = 0;
_inEscape = false;
_escFound = false;
_parameterCount = 0;
}
void termRestoreCursor(void) {
_cursor = _cursorSave;
}
void termSaveCursor(void) {
_cursorSave = _cursor;
}
void termScrollUp(void) {
int x;
int c = _columns * (_rows - 1);
int i = 0;
int j = _columns;
for (x=0; x < c; x++) {
_screenBuffer[i++] = _screenBuffer[j++];
}
for (x=0; x<_columns; x++) {
_screenBuffer[i].x = 32;
_screenBuffer[i++].y = 0;
}
termRepaint();
}
void termStart(jlStaT *font, byte xoff, byte yoff, byte cols, byte rows) {
_ansiFont = font;
_xoff = xoff;
_yoff = yoff;
_columns = cols;
_rows = rows;
_cursorSave.x = 1;
_cursorSave.y = 1;
_cursor.x = 1;
_cursor.y = 1;
_screenBuffer = (PositionT *)jlMalloc(sizeof(PositionT) * (size_t)(_columns * _rows));
termClearScreen();
}
void termStop(void) {
if (_screenBuffer != NULL) {
jlFree(_screenBuffer);
_screenBuffer = NULL;
}
}
void termUpdateColors(void) {
_foreground = _fColor + (_bold ? 8 : 0);
_background = _bColor;
}