#include #include #include #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 jlImgT *_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 _hiddenLines) { jlDrawBlit8x8(jlImgSurfaceGet(_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(jlImgSurfaceGet(_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(jlImgT *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; }