583 lines
12 KiB
C
583 lines
12 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "joey.h"
|
|
#include "ansiterm.h"
|
|
|
|
|
|
#ifdef JOEY_IIGS
|
|
segment "ansiterm";
|
|
#endif
|
|
|
|
// 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(jlStaSurfaceGet(_ansiFont), cx, cy, (_cursor.x - 1 + _xoff) * 8, (_cursor.y - 1 + _yoff) * 8);
|
|
}
|
|
}
|
|
|
|
|
|
void termRepaint(void) {
|
|
int i = _hiddenLines * _columns;
|
|
int x;
|
|
int y;
|
|
for (y=_hiddenLines; y<_rows; y++) {
|
|
for (x=0; x<_columns; x++) {
|
|
jlDrawBlit8x8(jlStaSurfaceGet(_ansiFont), _screenBuffer[i].x, _screenBuffer[i].y, (jint16)(x + _xoff) * 8, (jint16)(y + _yoff) * 8);
|
|
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;
|
|
}
|