#include <stdio.h>
#include <axwin3/axwin.h>
#include <axwin3/richtext.h>
+#include <stdbool.h>
+#include <assert.h>
#define UNIMPLIMENTED() do{_SysDebug("UNIMPLIMENTED %s", __func__); exit(-1);}while(0)
// === EXTERN ==
extern tHWND gMainWindow;
+typedef struct sLine tLine;
+
+struct sLine {
+ char *Data;
+ // TODO: Cache offsets to avoid scan-forward
+ size_t Len;
+ size_t Size;
+ bool IsDirty;
+};
+
+struct sTerminal {
+ int ViewCols;
+ int ViewRows;
+
+ int CursorRow;
+ int CursorCol;
+
+ size_t CursorByte;
+
+ bool bUsingAltBuf;
+
+ size_t ViewOffset;
+ size_t TotalLines;
+ struct sLine *PriBuf;
+
+ struct sLine *AltBuf;
+};
+
// === GLOBALS ===
- int giDisplayCols;
- int giDisplayLines;
- int giDisplayTotalLines;
- int giDisplayBufSize;
+tTerminal gMainBuffer;
int giCurrentLine;
int giCurrentLinePos; // byte offset, not column
int giCurrentCol;
char *gabDisplayLinesDirty;
// === CODE ===
-void Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
+tTerminal *Display_Init(int Cols, int Lines, int ExtraScrollbackLines)
{
- giDisplayCols = Cols;
- giDisplayLines = Lines;
- giDisplayTotalLines = Lines + ExtraScrollbackLines;
- gasDisplayLines = calloc( sizeof(char*), (Lines + ExtraScrollbackLines) );
- gaiDisplayLineSizes = calloc( sizeof(int), (Lines + ExtraScrollbackLines) );
- gabDisplayLinesDirty = calloc( sizeof(char), (Lines + ExtraScrollbackLines) );
+ tTerminal *term = &gMainBuffer;
+ term->ViewCols = Cols;
+ term->ViewRows = Lines;
+ term->TotalLines = Lines + ExtraScrollbackLines;
+ term->PriBuf = calloc( sizeof(tLine), (Lines + ExtraScrollbackLines) );
AxWin3_RichText_SetLineCount(gMainWindow, Lines+ExtraScrollbackLines);
AxWin3_RichText_SetCursorType(gMainWindow, 1); // TODO: enum
+ return term;
}
-void Display_int_PushString(int Length, const char *Text)
+// Return the byte length of a single on-screen character
+size_t _GetCharLength(size_t AvailLength, const char *Text, uint32_t *BaseCodepoint)
{
- _SysDebug("Line %i += %i '%*C'", giCurrentLine, Length, Length, Text);
- if( !gasDisplayLines[giCurrentLine] || giCurrentLinePos + Length >= gaiDisplayLineSizes[giCurrentLine] )
+ if( !AvailLength )
+ return 0;
+
+ size_t charlen = ReadUTF8(Text, BaseCodepoint);
+
+ while(charlen < AvailLength)
{
- int reqsize = giCurrentLinePos + Length;
- gaiDisplayLineSizes[giCurrentLine] = (reqsize + 32-1) & ~(32-1);
- void *tmp = realloc(gasDisplayLines[giCurrentLine], gaiDisplayLineSizes[giCurrentLine]);
- if( !tmp ) perror("Display_AddText - realloc");
- gasDisplayLines[giCurrentLine] = tmp;
+ uint32_t cp;
+ size_t size = ReadUTF8(Text+charlen, &cp);
+ if( Unicode_IsPrinting(cp) )
+ break;
+ charlen += size;
+ }
+
+ return charlen;
+}
+
+tLine *Display_int_GetCurLine(tTerminal *Term)
+{
+ int lineidx = Term->CursorRow + (Term->bUsingAltBuf ? 0 : Term->ViewOffset);
+ return (Term->bUsingAltBuf ? Term->AltBuf : Term->PriBuf) + lineidx;
+}
+
+size_t Display_int_PushCharacter(tTerminal *Term, size_t AvailLength, const char *Text)
+{
+ tLine *lineptr = Display_int_GetCurLine(Term);
+ uint32_t cp;
+ size_t charlen = _GetCharLength(AvailLength, Text, &cp);
+ bool bOverwrite = Unicode_IsPrinting(cp);
+
+ _SysDebug("Line %b:%i += %i '%.*s'", Term->bUsingAltBuf, Term->CursorRow, charlen, charlen, Text);
+
+ // Figure out how much we need to shift the stream
+ int shift;
+ if( bOverwrite ) {
+ size_t nextlen = _GetCharLength(
+ lineptr->Len-Term->CursorByte,
+ lineptr->Data+Term->CursorByte,
+ NULL);
+ _SysDebug("Char at +%i is %i long (%.*s)", Term->CursorByte, nextlen,
+ nextlen, lineptr->Data+Term->CursorByte);
+ shift = charlen - nextlen;
+ }
+ else {
+ shift = charlen;
+ }
+ _SysDebug("shift = %i", shift);
+
+ // Ensure we have space enough
+ if( !lineptr->Data || shift > 0 ) {
+ const size_t size_step = 64;
+ assert(shift > 0);
+ lineptr->Size = (lineptr->Len+shift+1 + size_step-1) & ~(size_step-1);
+ void *tmp = realloc(lineptr->Data, lineptr->Size);
+ if( !tmp ) perror("Display_int_PushCharacter - realloc");
+ lineptr->Data = tmp;
+ }
+
+ // Insert into stream
+ char *base = lineptr->Data + Term->CursorByte;
+ if( Term->CursorByte == lineptr->Len ) {
+ // No shifting needed
+ }
+ else if( shift >= 0 ) {
+ size_t bytes = lineptr->Len - (Term->CursorByte+shift);
+ _SysDebug("memmove(base+%i, base, %i)", shift, bytes);
+ memmove(base+shift, base, bytes);
+ }
+ else {
+ shift = -shift;
+ size_t bytes = lineptr->Len - (Term->CursorByte+shift);
+ _SysDebug("memmove(base, base+%i, %i)", shift, bytes);
+ memmove(base, base+shift, bytes);
}
+ memcpy(base, Text, charlen);
+ lineptr->IsDirty = true;
+ lineptr->Len += shift;
+ lineptr->Data[lineptr->Len] = 0; // NULL Terminate
- memcpy(gasDisplayLines[giCurrentLine]+giCurrentLinePos, Text, Length);
- gabDisplayLinesDirty[giCurrentLine] = 1;
- gasDisplayLines[giCurrentLine][giCurrentLinePos+Length] = 0;
- giCurrentLinePos += Length;
+ Term->CursorByte += charlen;
+ // HACKY: Prevents the CursorCol++ in Display_AddText from having an effect
+ if( !bOverwrite )
+ Term->CursorCol --;
+
+ return charlen;
}
-void Display_AddText(int Length, const char *UTF8Text)
+void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text)
{
_SysDebug("%i '%.*s'", Length, Length, UTF8Text);
- // Copy as many characters (not bytes, have to trim off the last char) as we can to the current line
- // - then roll over to the next line
while( Length > 0 )
{
- int space = giDisplayCols - giCurrentCol;
- int bytes = 0;
- while( space && bytes < Length )
- {
- uint32_t cp;
- bytes += ReadUTF8(UTF8Text+bytes, &cp);
- if( Unicode_IsPrinting(cp) ) {
- space --;
- giCurrentCol ++;
- }
- }
+ size_t used = Display_int_PushCharacter(Term, Length, UTF8Text);
- Display_int_PushString(bytes, UTF8Text);
-
- UTF8Text += bytes;
- Length -= bytes;
- if( Length != 0 )
- {
- // Next line
- giCurrentLinePos = 0;
- giCurrentCol = 0;
- giCurrentLine ++;
+ Length -= used;
+ UTF8Text += used;
+
+ Term->CursorCol ++;
+ if( Term->CursorCol == Term->ViewCols ) {
+ Display_Newline(Term, 1);
}
}
}
-void Display_Newline(int bCarriageReturn)
+void Display_Newline(tTerminal *Term, bool bCarriageReturn)
{
// Display_Flush();
// Going down!
- giCurrentLine ++;
- if( giCurrentLine == giDisplayLines )
- giCurrentLine = 0;
- if( giCurrentLine == giFirstLine )
- {
- giFirstLine ++;
- if(giFirstLine == giDisplayLines)
- giFirstLine = 0;
+ Term->CursorRow ++;
+ if( Term->CursorRow == Term->TotalLines ) {
+ // TODO: Scrolling
}
if( bCarriageReturn ) {
- giCurrentLinePos = 0;
- giCurrentCol = 0;
+ Term->CursorByte = 0;
+ Term->CursorCol = 0;
+ return ;
}
- else {
- giCurrentLinePos = 0;
- int i = giCurrentCol;
- if( !gasDisplayLines[giCurrentLine] )
- {
- giCurrentCol = 0;
- while(i--)
- Display_AddText(1, " ");
- }
- else
- {
- while( i -- )
- {
- uint32_t cp;
- giCurrentLinePos += ReadUTF8(gasDisplayLines[giCurrentLine]+giCurrentLinePos, &cp);
- if( !Unicode_IsPrinting(cp) )
- i ++;
- }
- }
+
+ tLine *line = Display_int_GetCurLine(Term);
+
+ Term->CursorByte = 0;
+ int old_col = Term->CursorCol;
+ Term->CursorCol = 0;
+
+ size_t ofs = 0;
+ while( Term->CursorCol < old_col && ofs < line->Len ) {
+ ofs += _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
+ Term->CursorCol ++;
}
+ Term->CursorByte = ofs;
+
+ while( Term->CursorCol < old_col )
+ Display_AddText(Term, 1, " ");
}
-void Display_SetCursor(int Row, int Col)
+void Display_SetCursor(tTerminal *Term, int Row, int Col)
{
UNIMPLIMENTED();
}
-void Display_MoveCursor(int RelRow, int RelCol)
+void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol)
{
- if( RelRow < 0 )
+ if( RelRow != 0 )
{
- for( ; RelRow < 0; RelRow ++ )
- {
- uint32_t cp;
- int delta = ReadUTF8Rev(gasDisplayLines[giCurrentLine], giCurrentLinePos, &cp);
- if( !Unicode_IsPrinting(cp) )
- RelRow --;
- else
- giCurrentCol --;
- giCurrentLinePos -= delta;
- }
+ UNIMPLIMENTED();
}
- else
+
+ if( RelCol != 0 )
{
- UNIMPLIMENTED();
+ int req_col = Term->CursorCol + RelCol;
+ if( req_col < 0 ) req_col = 0;
+ if( req_col > Term->ViewCols ) req_col = Term->ViewCols;
+
+ tLine *line = Display_int_GetCurLine(Term);
+ size_t ofs = 0;
+ for( int i = 0; i < req_col; i ++ )
+ {
+ size_t clen = _GetCharLength(line->Len-ofs, line->Data+ofs, NULL);
+ if( clen == 0 ) {
+ req_col = i;
+ break;
+ }
+ ofs += clen;
+ }
+
+ Term->CursorCol = req_col;
+ Term->CursorByte = ofs;
}
}
-void Display_ClearLine(int Dir) // 0: All, 1: Forward, -1: Reverse
+void Display_ClearLine(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
{
if( Dir == 0 )
{
+ tLine *line = Display_int_GetCurLine(Term);
// Completely clear line
- if( gasDisplayLines[giCurrentLine] )
- free(gasDisplayLines[giCurrentLine]);
- gasDisplayLines[giCurrentLine] = NULL;
- gabDisplayLinesDirty[giCurrentLine] = 1;
+ if( line->Data )
+ free(line->Data);
+ line->Data = NULL;
+ line->IsDirty = true;
}
else if( Dir == 1 )
{
}
}
-void Display_ClearLines(int Dir) // 0: All, 1: Forward, -1: Reverse
+void Display_ClearLines(tTerminal *Term, int Dir) // 0: All, 1: Forward, -1: Reverse
{
if( Dir == 0 )
{
}
}
-void Display_SetForeground(uint32_t RGB)
+void Display_SetForeground(tTerminal *Term, uint32_t RGB)
{
char buf[7+1];
sprintf(buf, "\1%06x", RGB&0xFFFFFF);
- Display_int_PushString(7, buf);
+ Display_AddText(Term, 7, buf);
}
-void Display_SetBackground(uint32_t RGB)
+void Display_SetBackground(tTerminal *Term, uint32_t RGB)
{
char buf[7+1];
sprintf(buf, "\2%06x", RGB&0xFFFFFF);
- Display_int_PushString(7, buf);
+ Display_AddText(Term, 7, buf);
}
-void Display_Flush(void)
+void Display_Flush(tTerminal *Term)
{
- int i;
- for( i = 0; i < giDisplayCols; i ++ )
+ for( int i = 0; i < Term->ViewRows; i ++ )
{
- int line = (giFirstLine + i) % giDisplayTotalLines;
- if( !gabDisplayLinesDirty[line] )
+ int line = (Term->ViewOffset + i) % Term->TotalLines;
+ tLine *lineptr = &Term->PriBuf[line];
+ if( !lineptr->IsDirty )
continue;
- _SysDebug("Line %i+%i '%s'", giFirstLine, i, gasDisplayLines[line]);
- AxWin3_RichText_SendLine(gMainWindow, giFirstLine + i, gasDisplayLines[line] );
- gabDisplayLinesDirty[line] = 0;
+ _SysDebug("Line %i+%i '%.*s'", Term->ViewOffset, i, lineptr->Len, lineptr->Data);
+ AxWin3_RichText_SendLine(gMainWindow, Term->ViewOffset + i, lineptr->Data );
+ lineptr->IsDirty = 0;
}
- AxWin3_RichText_SetCursorPos(gMainWindow, giCurrentLine, giCurrentCol);
+ AxWin3_RichText_SetCursorPos(gMainWindow, Term->CursorRow, Term->CursorCol);
}
-void Display_ShowAltBuffer(int AltBufEnabled)
+void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled)
{
UNIMPLIMENTED();
}
0xCCCCCC, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFFF
};
- int Term_HandleVT100_Long(int Len, const char *Buf);
+ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf);
static inline int min(int a, int b)
{
return a < b ? a : b;
}
-int Term_HandleVT100(int Len, const char *Buf)
+int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
{
#define MAX_VT100_ESCAPE_LEN 16
static char inc_buf[MAX_VT100_ESCAPE_LEN];
switch(inc_buf[1])
{
case '[': // Multibyte, funtime starts
- ret = Term_HandleVT100_Long(inc_len-2, inc_buf+2);
+ ret = Term_HandleVT100_Long(Term, inc_len-2, inc_buf+2);
if( ret > 0 ) {
ret += 2;
}
switch( *Buf )
{
case '\b':
- Display_MoveCursor(-1, 0);
- Display_AddText(1, " ");
- Display_MoveCursor(-1, 0);
+ Display_MoveCursor(Term, -1, 0);
+ Display_AddText(Term, 1, " ");
+ Display_MoveCursor(Term, -1, 0);
// TODO: Need to handle \t and ^A-Z
return 1;
case '\t':
// TODO: tab (get current cursor pos, space until multiple of 8)
return 1;
case '\n':
- Display_Newline(1);
+ Display_Newline(Term, 1);
return 1;
case '\r':
- Display_MoveCursor(INT_MIN, 0);
+ Display_MoveCursor(Term, INT_MIN, 0);
return 1;
}
return -ret;
}
-int Term_HandleVT100_Long(int Len, const char *Buffer)
+int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
{
char c;
int argc = 0, j = 0;
if( bQuestionMark )
{
+ int set = 0;
// Special commands
switch( c )
{
+ case 'h': // set
+ set = 1;
+ case 'l': // unset
+ switch(args[0])
+ {
+ case 25: // Hide cursor
+ _SysDebug("TODO: \\e[?25%c Show/Hide cursor", c);
+ break;
+ case 1047: // Alternate buffer
+ Display_ShowAltBuffer(Term, set);
+ break;
+ default:
+ _SysDebug("TODO: \\e[?%i%c Unknow DEC private mode", args[0], c);
+ break;
+ }
+ break;
default:
_SysDebug("Unknown VT100 extended escape char 0x%x", c);
break;
// Standard commands
switch( c )
{
+ case 'H':
+ if( argc != 2 ) {
+ }
+ else {
+ Display_SetCursor(Term, args[0], args[1]);
+ }
+ break;
case 'J':
if( argc == 0 )
- Display_ClearLine(0);
+ Display_ClearLine(Term, 0);
else if( args[0] == 2 )
- Display_ClearLines(0); // Entire screen!
+ Display_ClearLines(Term, 0); // Entire screen!
else
_SysDebug("TODO: VT100 %i J", args[0]);
break;
+ case 'T': // Scroll down n=1
+ _SysDebug("TODO: \\x1B[nT - Scroll down");
+ break;
case 'm':
if( argc == 0 )
{
else if( 30 <= args[i] && args[i] <= 37 )
{
// TODO: Bold/bright
- Display_SetForeground( caVT100Colours[ args[i]-30 ] );
+ Display_SetForeground( Term, caVT100Colours[ args[i]-30 ] );
}
else if( 40 <= args[i] && args[i] <= 47 )
{
// TODO: Bold/bright
- Display_SetBackground( caVT100Colours[ args[i]-30 ] );
+ Display_SetBackground( Term, caVT100Colours[ args[i]-30 ] );
}
}
}
break;
+ // Set scrolling region
+ case 'r':
+ _SysDebug("TODO: \\x1B[%i;%ir - Set Scroll Region",
+ args[0], args[1]);
+ break;
default:
- _SysDebug("Unknown VT100 escape char 0x%x", c);
+ _SysDebug("Unknown VT100 long escape char 0x%x '%c'", c, c);
break;
}
}