* drv/vterm_termbuf.c
* - Virtual Terminal - Terminal buffer manipulation
*/
+#define DEBUG 0
+#define DEBUG_CHECKHEAP 0
#include "vterm.h"
+#if DEBUG_CHECKHEAP
+# define HEAP_VALIDATE() Heap_Validate()
+#else
+# define HEAP_VALIDATE() do{}while(0)
+#endif
+
+extern int Term_HandleVT100(tVTerm *Term, int Len, const char *Buf);
+
// === CODE ===
/**
*/
void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
{
- Uint32 val;
- int i;
-
// Iterate
- for( i = 0; i < Count; i++ )
+ for( int ofs = 0; ofs < Count; )
{
- // Handle escape sequences
- if( Buffer[i] == 0x1B )
- {
- i ++;
- i += VT_int_ParseEscape(Term, (const char*)&Buffer[i]) - 1;
- continue;
+ int esc_len = Term_HandleVT100(Term, Count - ofs, (const void*)(Buffer + ofs));
+ if( esc_len < 0 ) {
+ esc_len = -esc_len;
+ //LOG("%i '%*C'", esc_len, esc_len, Buffer+ofs);
+ LOG("%i '%.*s'", esc_len, esc_len, Buffer+ofs);
+ VT_int_PutRawString(Term, Buffer + ofs, esc_len);
+ //Debug("Raw string '%.*s'", esc_len, Buffer+ofs);
}
-
- // Fast check for non UTF-8
- if( Buffer[i] < 128 ) // Plain ASCII
- VT_int_PutChar(Term, Buffer[i]);
- else { // UTF-8
- i += ReadUTF8(&Buffer[i], &val) - 1;
- VT_int_PutChar(Term, val);
+ else {
+ //Debug("Escape code '%.*s'", esc_len, Buffer+ofs);
}
+ ASSERTCR(esc_len, >, 0, );
+ ofs += esc_len;
}
// Update Screen
- VT_int_UpdateScreen( Term, 0 );
+ VT_int_UpdateScreen( Term, 1 );
+}
+
+void VT_int_PutRawString(tVTerm *Term, const Uint8 *String, size_t Bytes)
+{
+ for( int i = 0; i < Bytes; ) {
+ Uint32 val;
+ i += ReadUTF8(String+i, &val);
+ VT_int_PutChar(Term, val);
+ }
}
/**
void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
{
int i;
- tVT_Char *buffer;
- int write_pos;
- if(Term->Flags & VT_FLAG_ALTBUF) {
- buffer = Term->AltBuf;
- write_pos = Term->AltWritePos;
+ HEAP_VALIDATE();
+
+ size_t limit = VT_int_GetBufferRows(Term) * Term->TextWidth;
+ size_t write_pos = *VT_int_GetWritePosPtr(Term);
+ tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
+
+ ASSERTC(write_pos, >=, 0);
+
+ // Scroll entire buffer (about to write outside limit)
+ if( write_pos >= limit )
+ {
+ ASSERTC(write_pos, <, limit + Term->TextWidth);
+ VT_int_ScrollText(Term, 1);
+ write_pos -= Term->TextWidth;
}
- else {
- buffer = Term->Text;
- write_pos = Term->WritePos;
+
+ // Bring written cell into view
+ if( !(Term->Flags & VT_FLAG_ALTBUF) )
+ {
+ size_t onescreen = Term->TextWidth*Term->TextHeight;
+ if( write_pos >= Term->ViewPos + onescreen )
+ {
+ size_t new_pos = write_pos - (write_pos % Term->TextWidth) - onescreen + Term->TextWidth;
+ size_t count = (new_pos - Term->ViewPos) / Term->TextWidth;
+ VT_int_ScrollFramebuffer(Term, count);
+ //Debug("VT_int_PutChar: VScroll down to %i", new_pos/Term->TextWidth);
+ Term->ViewPos = new_pos;
+ }
+ else if( write_pos < Term->ViewPos )
+ {
+ size_t new_pos = write_pos - (write_pos % Term->TextWidth);
+ size_t count = (Term->ViewPos - new_pos) / Term->TextWidth;
+ VT_int_ScrollFramebuffer(Term, -count);
+ //Debug("VT_int_PutChar: VScroll up to %i", new_pos/Term->TextWidth);
+ Term->ViewPos = new_pos;
+ }
+ else
+ {
+ // no action, cell is visible
+ }
}
switch(Ch)
{
- case '\0': return; // Ignore NULL byte
+ case '\0': // Ignore NULL byte
+ return;
case '\n':
VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
write_pos += Term->TextWidth;
+ // TODO: Scroll display down if needed
case '\r':
write_pos -= write_pos % Term->TextWidth;
break;
case '\t': {
- int line = write_pos / Term->TextWidth;
- write_pos %= Term->TextWidth;
+ size_t col = write_pos % Term->TextWidth;
do {
buffer[ write_pos ].Ch = '\0';
buffer[ write_pos ].Colour = Term->CurColour;
write_pos ++;
- } while(write_pos & 7);
- write_pos += line * Term->TextWidth;
+ col ++;
+ } while( (col & 7) && col < Term->TextWidth );
break; }
case '\b':
break;
}
- if(Term->Flags & VT_FLAG_ALTBUF)
- {
- Term->AltWritePos = write_pos;
-
- if(Term->AltWritePos >= Term->TextWidth*Term->TextHeight)
- {
- Term->AltWritePos -= Term->TextWidth;
- VT_int_ScrollText(Term, 1);
- }
-
- }
- else
- {
- Term->WritePos = write_pos;
- // Move Screen
- // - Check if we need to scroll the entire scrollback buffer
- if(Term->WritePos >= Term->TextWidth*Term->TextHeight*(giVT_Scrollback+1))
- {
- int base;
-
- // Update view position
- base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback);
- if(Term->ViewPos < base)
- Term->ViewPos += Term->Width;
- if(Term->ViewPos > base)
- Term->ViewPos = base;
-
- VT_int_ScrollText(Term, 1);
- Term->WritePos -= Term->TextWidth;
- }
- // Ok, so we only need to scroll the screen
- else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight)
- {
- VT_int_ScrollFramebuffer( Term, 1 );
-
- Term->ViewPos += Term->TextWidth;
- }
- }
+ ASSERTC(write_pos, <=, limit);
+ *VT_int_GetWritePosPtr(Term) = write_pos;
+
+ HEAP_VALIDATE();
//LEAVE('-');
}
void VT_int_ScrollText(tVTerm *Term, int Count)
{
tVT_Char *buf;
- int height, init_write_pos;
- int len, i;
int scroll_top, scroll_height;
+
+ HEAP_VALIDATE();
// Get buffer pointer and attributes
+ size_t height = VT_int_GetBufferRows(Term);
+ size_t *write_pos_ptr = VT_int_GetWritePosPtr(Term);
+
if( Term->Flags & VT_FLAG_ALTBUF )
{
buf = Term->AltBuf;
- height = Term->TextHeight;
- init_write_pos = Term->AltWritePos;
scroll_top = Term->ScrollTop;
scroll_height = Term->ScrollHeight;
}
else
{
buf = Term->Text;
- height = Term->TextHeight*(giVT_Scrollback+1);
- init_write_pos = Term->WritePos;
scroll_top = 0;
scroll_height = height;
}
+
+ const int init_write_pos = *write_pos_ptr;
// Scroll text upwards (more space at bottom)
if( Count > 0 )
{
- int base;
-
// Set up
if(Count > scroll_height) Count = scroll_height;
- base = Term->TextWidth*(scroll_top + scroll_height - Count);
- len = Term->TextWidth*(scroll_height - Count);
+ size_t chars = Term->TextWidth*Count;
+ size_t base = Term->TextWidth*scroll_top;
+ size_t len = Term->TextWidth*(scroll_height - Count);
// Scroll terminal cache
- memmove(
- &buf[Term->TextWidth*scroll_top],
- &buf[Term->TextWidth*(scroll_top+Count)],
- len*sizeof(tVT_Char)
- );
+ ASSERTC( base + chars + len, <=, Term->TextWidth*height );
+ memmove( &buf[base], &buf[base+chars], len*sizeof(tVT_Char) );
+
// Clear last rows
- for( i = 0; i < Term->TextWidth*Count; i ++ )
+ for( int i = 0; i < chars; i ++ )
{
- buf[ base + i ].Ch = 0;
- buf[ base + i ].Colour = Term->CurColour;
+ ASSERTC(base + len + i, <, Term->TextWidth*height);
+ buf[ base + len + i ].Ch = 0;
+ buf[ base + len + i ].Colour = Term->CurColour;
}
// Update Screen
Term->AltWritePos = base;
else
Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
- for( i = 0; i < Count; i ++ )
+ for( int i = 0; i < Count; i ++ )
{
VT_int_UpdateScreen( Term, 0 );
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos += Term->TextWidth;
- else
- Term->WritePos += Term->TextWidth;
+ *write_pos_ptr += Term->TextWidth;
}
}
else
Count = -Count;
if(Count > scroll_height) Count = scroll_height;
- len = Term->TextWidth*(scroll_height - Count);
+ size_t chars = Term->TextWidth*Count;
+ size_t base = Term->TextWidth*scroll_top;
+ size_t len = Term->TextWidth*scroll_height - chars;
// Scroll terminal cache
- memmove(
- &buf[Term->TextWidth*(scroll_top+Count)],
- &buf[Term->TextWidth*scroll_top],
- len*sizeof(tVT_Char)
- );
+ ASSERTC( base + chars + len, <=, Term->TextWidth*height );
+ memmove( &buf[base+chars], &buf[base], len*sizeof(tVT_Char) );
+
// Clear preceding rows
- for( i = 0; i < Term->TextWidth*Count; i ++ )
+ for( int i = 0; i < chars; i ++ )
{
buf[ i ].Ch = 0;
buf[ i ].Colour = Term->CurColour;
}
+ // Update screen (shift framebuffer, re-render revealed lines)
VT_int_ScrollFramebuffer( Term, -Count );
if( Term->Flags & VT_FLAG_ALTBUF )
Term->AltWritePos = Term->TextWidth*scroll_top;
else
Term->WritePos = Term->ViewPos;
- for( i = 0; i < Count; i ++ )
+ for( int i = 0; i < Count; i ++ )
{
VT_int_UpdateScreen( Term, 0 );
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos += Term->TextWidth;
- else
- Term->WritePos += Term->TextWidth;
}
}
- if( Term->Flags & VT_FLAG_ALTBUF )
- Term->AltWritePos = init_write_pos;
- else
- Term->WritePos = init_write_pos;
+ *write_pos_ptr = init_write_pos;
+
+ HEAP_VALIDATE();
}
/**
* \param Term Terminal to modify
* \param Num Line number to clear
*/
-void VT_int_ClearLine(tVTerm *Term, int Num)
+void VT_int_ClearLine(tVTerm *Term, int Row)
{
- int i;
- tVT_Char *cell;
+ HEAP_VALIDATE();
- if( Num < 0 || Num >= Term->TextHeight * (giVT_Scrollback + 1) ) return ;
+ size_t height = VT_int_GetBufferRows(Term);
+ tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
+ ASSERTCR(Row, >=, 0, );
+ ASSERTCR(Row, <, height, );
- cell = (Term->Flags & VT_FLAG_ALTBUF) ? Term->AltBuf : Term->Text;
- cell = &cell[ Num*Term->TextWidth ];
+ size_t base = Row * Term->TextWidth;
- for( i = Term->TextWidth; i--; )
+ for( int i = 0; i < Term->TextWidth; i ++ )
{
- cell[ i ].Ch = 0;
- cell[ i ].Colour = Term->CurColour;
+ buffer[ base + i ].Ch = 0;
+ buffer[ base + i ].Colour = Term->CurColour;
}
+
+ HEAP_VALIDATE();
+}
+
+void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
+{
+ HEAP_VALIDATE();
+
+ size_t height = VT_int_GetBufferRows(Term);
+ tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
+ ASSERTCR(Row, >=, 0, );
+ ASSERTCR(Row, <, height, );
+
+ ASSERTCR(FirstCol, <=, LastCol, );
+ ASSERTCR(FirstCol, <, Term->TextWidth, );
+ ASSERTCR(LastCol, <=, Term->TextWidth, );
+
+ size_t base = Row * Term->TextWidth;
+ for( int i = FirstCol; i < LastCol; i ++ )
+ {
+ ASSERTC(base + i, <, height * Term->TextWidth);
+ buffer[ base + i ].Ch = 0;
+ buffer[ base + i ].Colour = Term->CurColour;
+ }
+
+ HEAP_VALIDATE();
}
/**
* \param NewWidth New framebuffer width
* \param NewHeight New framebuffer height
*/
-void VT_int_ChangeMode(tVTerm *Term, int NewMode, int NewWidth, int NewHeight)
+void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
{
int oldW = Term->Width;
int oldTW = Term->TextWidth;
int oldTH = Term->TextHeight;
tVT_Char *oldTBuf = Term->Text;
Uint32 *oldFB = Term->Buffer;
- int w, h, i;
+ int w, h;
+
+ HEAP_VALIDATE();
// TODO: Increase RealWidth/RealHeight when this happens
if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth;
if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
- Term->Mode = NewMode;
-
// Fast exit if no resolution change
if(NewWidth == Term->Width && NewHeight == Term->Height)
return ;
w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
h *= giVT_Scrollback + 1;
- for( i = 0; i < h; i ++ )
+ for( int i = 0; i < h; i ++ )
{
memcpy(
&Term->Text[i*Term->TextWidth],
// Copy old buffer
w = (oldW > Term->Width) ? Term->Width : oldW;
h = (oldH > Term->Height) ? Term->Height : oldH;
- for( i = 0; i < h; i ++ )
+ for( int i = 0; i < h; i ++ )
{
memcpy(
&Term->Buffer[i*Term->Width],
}
// Debug
- switch(NewMode)
+ #if 0
+ switch(Term->Mode)
{
case TERM_MODE_TEXT:
Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
//case TERM_MODE_3DACCEL:
// return;
}
+ #endif
+
+ HEAP_VALIDATE();
}
VT_int_UpdateScreen(Term, 1);
}
+size_t *VT_int_GetWritePosPtr(tVTerm *Term)
+{
+ return ((Term->Flags & VT_FLAG_ALTBUF) ? &Term->AltWritePos : &Term->WritePos);
+}
+
+size_t VT_int_GetBufferRows(tVTerm *Term)
+{
+ return ((Term->Flags & VT_FLAG_ALTBUF) ? 1 : (giVT_Scrollback+1))*Term->TextHeight;
+}
+