3 * - By John Hodge (thePowersGang)
6 * - Virtual Terminal - Terminal buffer manipulation
9 #define DEBUG_CHECKHEAP 0
13 # define HEAP_VALIDATE() Heap_Validate()
15 # define HEAP_VALIDATE() do{}while(0)
18 extern int Term_HandleVT100(tVTerm *Term, int Len, const char *Buf);
23 * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
24 * \brief Print a string to the Virtual Terminal
26 void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
28 ENTER("pTerm pBuffer iCount", Term, Buffer, Count);
30 for( int ofs = 0; ofs < Count; )
32 int esc_len = Term_HandleVT100(Term, Count - ofs, (const void*)(Buffer + ofs));
35 //LOG("%i '%*C'", esc_len, esc_len, Buffer+ofs);
36 LOG("%i '%.*s'", esc_len, esc_len, Buffer+ofs);
37 VT_int_PutRawString(Term, Buffer + ofs, esc_len);
38 //Debug("Raw string '%.*s'", esc_len, Buffer+ofs);
41 //Debug("Escape code '%.*s'", esc_len, Buffer+ofs);
43 ASSERTCR(esc_len, >, 0, );
48 VT_int_UpdateScreen( Term, 0 );
52 void VT_int_PutRawString(tVTerm *Term, const Uint8 *String, size_t Bytes)
54 for( int i = 0; i < Bytes; ) {
56 i += ReadUTF8(String+i, &val);
57 VT_int_PutChar(Term, val);
62 * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
63 * \brief Write a single character to a VTerm
65 void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
71 const size_t maxrows = VT_int_GetBufferRows(Term);
72 const size_t limit = maxrows * Term->TextWidth;
73 tVT_Pos *wrpos = VT_int_GetWritePosPtr(Term);
74 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
76 // If writing with the cursor on righthand edge, wrap down to next line
77 // TODO: Wrap to same line?
78 if( wrpos->Col >= Term->TextWidth )
80 ASSERTC(wrpos->Col, <=, Term->TextWidth);
81 VT_int_UpdateScreen( Term, 0 );
86 // Scroll entire buffer (about to write outside limit)
87 if( wrpos->Row >= maxrows )
89 ASSERTC(wrpos->Row, <, maxrows + 1);
90 VT_int_ScrollText(Term, 1);
94 // Bring written cell into view
95 if( !(Term->Flags & VT_FLAG_ALTBUF) )
97 if( wrpos->Row >= Term->ViewTopRow + Term->TextHeight )
99 size_t new_pos = wrpos->Row - (Term->TextHeight - 1);
100 size_t count = new_pos - Term->ViewTopRow;
101 VT_int_ScrollFramebuffer(Term, count);
102 //Debug("VT_int_PutChar: VScroll down to %i", new_pos);
103 Term->ViewTopRow = new_pos;
105 else if( wrpos->Row < Term->ViewTopRow )
107 size_t new_pos = wrpos->Row;
108 size_t count = Term->ViewTopRow - new_pos;
109 VT_int_ScrollFramebuffer(Term, -count);
110 //Debug("VT_int_PutChar: VScroll up to %i", new_pos);
111 Term->ViewTopRow = new_pos;
115 // no action, cell is visible
119 size_t write_pos = wrpos->Row * Term->TextWidth + wrpos->Col;
120 ASSERTC(write_pos, <, limit);
124 case '\0': // Ignore NULL byte
127 LOG("Newline, update @ %i", write_pos);
128 VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
130 // TODO: Force scroll?
136 size_t col = wrpos->Col;
138 buffer[ write_pos ].Ch = '\0';
139 buffer[ write_pos ].Colour = Term->CurColour;
142 } while( (col & 7) && col < Term->TextWidth );
146 // Backspace is invalid at Offset 0
147 if(write_pos == 0) break;
151 if(buffer[ write_pos ].Ch != '\0') {
152 buffer[ write_pos ].Ch = 0;
153 buffer[ write_pos ].Colour = Term->CurColour;
157 i = 7; // Limit it to 8
159 buffer[ write_pos ].Ch = 0;
160 buffer[ write_pos ].Colour = Term->CurColour;
162 } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
163 if(buffer[ write_pos ].Ch != '\0')
165 wrpos->Col = write_pos % Term->TextWidth;
169 buffer[ write_pos ].Ch = Ch;
170 buffer[ write_pos ].Colour = Term->CurColour;
171 // Update the line before wrapping
172 if( (write_pos + 1) % Term->TextWidth == 0 ) {
173 LOG("Line wrap, update @ %i", write_pos);
174 VT_int_UpdateScreen( Term, 0 );
175 // NOTE: Code at the top of PutChar handles the actual wrapping
182 ASSERTC(write_pos, <=, limit);
186 // TODO: Schedule a delayed screen update
189 void VT_int_ScrollText(tVTerm *Term, int Count)
191 ENTER("pTerm iCount", Term, Count);
193 int scroll_top, scroll_height;
197 // Get buffer pointer and attributes
198 size_t height = VT_int_GetBufferRows(Term);
199 tVT_Pos *wrpos = VT_int_GetWritePosPtr(Term);
201 if( Term->Flags & VT_FLAG_ALTBUF )
204 scroll_top = Term->ScrollTop;
205 scroll_height = Term->ScrollHeight;
211 scroll_height = height;
214 const tVT_Pos init_wrpos = *wrpos;
216 // Scroll text upwards (more space at bottom)
220 if(Count > scroll_height) Count = scroll_height;
221 size_t chars = Term->TextWidth*Count;
222 size_t base = Term->TextWidth*scroll_top;
223 size_t len = Term->TextWidth*(scroll_height - Count);
225 // Scroll terminal cache
226 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
227 memmove( &buf[base], &buf[base+chars], len*sizeof(tVT_Char) );
230 for( int i = 0; i < chars; i ++ )
232 ASSERTC(base + len + i, <, Term->TextWidth*height);
233 buf[ base + len + i ].Ch = 0;
234 buf[ base + len + i ].Colour = Term->CurColour;
238 VT_int_ScrollFramebuffer( Term, Count );
239 if( Term->Flags & VT_FLAG_ALTBUF )
240 wrpos->Row = scroll_top;
242 wrpos->Row = Term->ViewTopRow + (Term->TextHeight - Count);
243 for( int i = 0; i < Count; i ++ )
245 VT_int_UpdateScreen( Term, 0 );
252 if(Count > scroll_height) Count = scroll_height;
254 size_t chars = Term->TextWidth*Count;
255 size_t base = Term->TextWidth*scroll_top;
256 size_t len = Term->TextWidth*scroll_height - chars;
258 // Scroll terminal cache
259 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
260 memmove( &buf[base+chars], &buf[base], len*sizeof(tVT_Char) );
262 // Clear preceding rows
263 for( int i = 0; i < chars; i ++ )
266 buf[ i ].Colour = Term->CurColour;
269 // Update screen (shift framebuffer, re-render revealed lines)
270 VT_int_ScrollFramebuffer( Term, -Count );
271 if( Term->Flags & VT_FLAG_ALTBUF )
272 wrpos->Row = scroll_top;
274 wrpos->Row = Term->ViewTopRow;
275 for( int i = 0; i < Count; i ++ )
277 VT_int_UpdateScreen( Term, 0 );
289 * \brief Clears a line in a virtual terminal
290 * \param Term Terminal to modify
291 * \param Num Line number to clear
293 void VT_int_ClearLine(tVTerm *Term, int Row)
297 size_t height = VT_int_GetBufferRows(Term);
298 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
299 ASSERTCR(Row, >=, 0, );
300 ASSERTCR(Row, <, height, );
302 size_t base = Row * Term->TextWidth;
304 for( int i = 0; i < Term->TextWidth; i ++ )
306 buffer[ base + i ].Ch = 0;
307 buffer[ base + i ].Colour = Term->CurColour;
313 void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
317 size_t height = VT_int_GetBufferRows(Term);
318 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
319 ASSERTCR(Row, >=, 0, );
320 ASSERTCR(Row, <, height, );
322 ASSERTCR(FirstCol, <=, LastCol, );
323 ASSERTCR(FirstCol, <, Term->TextWidth, );
324 ASSERTCR(LastCol, <=, Term->TextWidth, );
326 size_t base = Row * Term->TextWidth;
327 for( int i = FirstCol; i < LastCol; i ++ )
329 ASSERTC(base + i, <, height * Term->TextWidth);
330 buffer[ base + i ].Ch = 0;
331 buffer[ base + i ].Colour = Term->CurColour;
338 * \brief Update the screen mode
339 * \param Term Terminal to update
340 * \param NewMode New mode to set
341 * \param NewWidth New framebuffer width
342 * \param NewHeight New framebuffer height
344 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
346 int oldW = Term->Width;
347 int oldTW = Term->TextWidth;
348 int oldH = Term->Height;
349 int oldTH = Term->TextHeight;
350 tVT_Char *oldTBuf = Term->Text;
351 Uint32 *oldFB = Term->Buffer;
356 // TODO: Increase RealWidth/RealHeight when this happens
357 if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth;
358 if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
360 // Fast exit if no resolution change
361 if(NewWidth == Term->Width && NewHeight == Term->Height)
364 // Calculate new dimensions
365 Term->Width = NewWidth;
366 Term->Height = NewHeight;
367 Term->TextWidth = NewWidth / giVT_CharWidth;
368 Term->TextHeight = NewHeight / giVT_CharHeight;
369 Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
371 // Allocate new buffers
374 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
379 w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
380 h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
381 h *= giVT_Scrollback + 1;
382 for( int i = 0; i < h; i ++ )
385 &Term->Text[i*Term->TextWidth],
394 Term->AltBuf = realloc(
396 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
401 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
403 w = (oldW > Term->Width) ? Term->Width : oldW;
404 h = (oldH > Term->Height) ? Term->Height : oldH;
405 for( int i = 0; i < h; i ++ )
408 &Term->Buffer[i*Term->Width],
421 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
422 Term, Term->TextWidth, Term->TextHeight);
425 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
426 Term, Term->Width, Term->Height);
428 //case TERM_MODE_2DACCEL:
429 //case TERM_MODE_3DACCEL:
438 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
441 Term->Flags |= VT_FLAG_ALTBUF;
443 Term->Flags &= ~VT_FLAG_ALTBUF;
444 VT_int_UpdateScreen(Term, 1);
447 tVT_Pos *VT_int_GetWritePosPtr(tVTerm *Term)
449 return ((Term->Flags & VT_FLAG_ALTBUF) ? &Term->AltWritePos : &Term->WritePos);
452 size_t VT_int_GetBufferRows(tVTerm *Term)
454 return ((Term->Flags & VT_FLAG_ALTBUF) ? 1 : (giVT_Scrollback+1))*Term->TextHeight;