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)
29 for( int ofs = 0; ofs < Count; )
31 int esc_len = Term_HandleVT100(Term, Count - ofs, (const void*)(Buffer + ofs));
34 //LOG("%i '%*C'", esc_len, esc_len, Buffer+ofs);
35 LOG("%i '%.*s'", esc_len, esc_len, Buffer+ofs);
36 VT_int_PutRawString(Term, Buffer + ofs, esc_len);
37 //Debug("Raw string '%.*s'", esc_len, Buffer+ofs);
40 //Debug("Escape code '%.*s'", esc_len, Buffer+ofs);
42 ASSERTCR(esc_len, >, 0, );
46 VT_int_UpdateScreen( Term, 1 );
49 void VT_int_PutRawString(tVTerm *Term, const Uint8 *String, size_t Bytes)
51 for( int i = 0; i < Bytes; ) {
53 i += ReadUTF8(String+i, &val);
54 VT_int_PutChar(Term, val);
59 * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
60 * \brief Write a single character to a VTerm
62 void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
71 if(Term->Flags & VT_FLAG_ALTBUF) {
72 buffer = Term->AltBuf;
73 write_pos = Term->AltWritePos;
74 limit = Term->TextHeight * Term->TextWidth;
78 write_pos = Term->WritePos;
79 limit = Term->TextHeight*(giVT_Scrollback+1) * Term->TextWidth;
82 // TODO: Can the write position be equal to the end of screen?
83 ASSERTC(write_pos, <, limit);
87 case '\0': // Ignore NULL byte
90 VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
91 write_pos += Term->TextWidth;
93 write_pos -= write_pos % Term->TextWidth;
97 size_t col = write_pos % Term->TextWidth;
99 buffer[ write_pos ].Ch = '\0';
100 buffer[ write_pos ].Colour = Term->CurColour;
103 } while( (col & 7) && col < Term->TextWidth );
107 // Backspace is invalid at Offset 0
108 if(write_pos == 0) break;
112 if(buffer[ write_pos ].Ch != '\0') {
113 buffer[ write_pos ].Ch = 0;
114 buffer[ write_pos ].Colour = Term->CurColour;
118 i = 7; // Limit it to 8
120 buffer[ write_pos ].Ch = 0;
121 buffer[ write_pos ].Colour = Term->CurColour;
123 } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
124 if(buffer[ write_pos ].Ch != '\0')
129 buffer[ write_pos ].Ch = Ch;
130 buffer[ write_pos ].Colour = Term->CurColour;
131 // Update the line before wrapping
132 if( (write_pos + 1) % Term->TextWidth == 0 )
133 VT_int_UpdateScreen( Term, 0 );
138 if(Term->Flags & VT_FLAG_ALTBUF)
140 Term->AltWritePos = write_pos;
142 if(Term->AltWritePos >= limit)
144 Term->AltWritePos -= Term->TextWidth;
145 VT_int_ScrollText(Term, 1);
150 Term->WritePos = write_pos;
152 // - Check if we need to scroll the entire scrollback buffer
153 if(Term->WritePos >= limit)
157 // Update view position
158 base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback);
159 if(Term->ViewPos < base)
160 Term->ViewPos += Term->Width;
161 if(Term->ViewPos > base)
162 Term->ViewPos = base;
164 VT_int_ScrollText(Term, 1);
165 Term->WritePos -= Term->TextWidth;
167 // Ok, so we only need to scroll the screen
168 else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight)
170 VT_int_ScrollFramebuffer( Term, 1 );
172 Term->ViewPos += Term->TextWidth;
181 void VT_int_ScrollText(tVTerm *Term, int Count)
186 int scroll_top, scroll_height;
190 // Get buffer pointer and attributes
191 if( Term->Flags & VT_FLAG_ALTBUF )
194 height = Term->TextHeight;
195 write_pos_ptr = &Term->AltWritePos;
196 scroll_top = Term->ScrollTop;
197 scroll_height = Term->ScrollHeight;
202 height = Term->TextHeight*(giVT_Scrollback+1);
203 write_pos_ptr = &Term->WritePos;
205 scroll_height = height;
208 const int init_write_pos = *write_pos_ptr;
210 // Scroll text upwards (more space at bottom)
214 if(Count > scroll_height) Count = scroll_height;
215 size_t chars = Term->TextWidth*Count;
216 size_t base = Term->TextWidth*scroll_top;
217 size_t len = Term->TextWidth*(scroll_height - Count);
219 // Scroll terminal cache
220 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
221 memmove( &buf[base], &buf[base+chars], len*sizeof(tVT_Char) );
224 for( int i = 0; i < chars; i ++ )
226 ASSERTC(base + len + i, <, Term->TextWidth*height);
227 buf[ base + len + i ].Ch = 0;
228 buf[ base + len + i ].Colour = Term->CurColour;
232 VT_int_ScrollFramebuffer( Term, Count );
233 if( Term->Flags & VT_FLAG_ALTBUF )
234 Term->AltWritePos = base;
236 Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
237 for( int i = 0; i < Count; i ++ )
239 VT_int_UpdateScreen( Term, 0 );
240 *write_pos_ptr += Term->TextWidth;
246 if(Count > scroll_height) Count = scroll_height;
248 size_t chars = Term->TextWidth*Count;
249 size_t base = Term->TextWidth*scroll_top;
250 size_t len = Term->TextWidth*scroll_height - chars;
252 // Scroll terminal cache
253 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
254 memmove( &buf[base+chars], &buf[base], len*sizeof(tVT_Char) );
256 // Clear preceding rows
257 for( int i = 0; i < chars; i ++ )
260 buf[ i ].Colour = Term->CurColour;
263 // Update screen (shift framebuffer, re-render revealed lines)
264 VT_int_ScrollFramebuffer( Term, -Count );
265 if( Term->Flags & VT_FLAG_ALTBUF )
266 Term->AltWritePos = Term->TextWidth*scroll_top;
268 Term->WritePos = Term->ViewPos;
269 for( int i = 0; i < Count; i ++ )
271 VT_int_UpdateScreen( Term, 0 );
275 *write_pos_ptr = init_write_pos;
281 * \brief Clears a line in a virtual terminal
282 * \param Term Terminal to modify
283 * \param Num Line number to clear
285 void VT_int_ClearLine(tVTerm *Term, int Row)
289 size_t height = Term->TextHeight * (Term->Flags & VT_FLAG_ALTBUF ? 1 : giVT_Scrollback + 1);
290 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
291 ASSERTCR(Row, >=, 0, );
292 ASSERTCR(Row, <, height, );
294 size_t base = Row * Term->TextWidth;
296 for( int i = 0; i < Term->TextWidth; i ++ )
298 buffer[ base + i ].Ch = 0;
299 buffer[ base + i ].Colour = Term->CurColour;
305 void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
309 size_t height = Term->TextHeight * (Term->Flags & VT_FLAG_ALTBUF ? 1 : giVT_Scrollback + 1);
310 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
311 ASSERTCR(Row, >=, 0, );
312 ASSERTCR(Row, <, height, );
314 ASSERTCR(FirstCol, <=, LastCol, );
315 ASSERTCR(FirstCol, <, Term->TextWidth, );
316 ASSERTCR(LastCol, <=, Term->TextWidth, );
318 size_t base = Row * Term->TextWidth;
319 for( int i = FirstCol; i < LastCol; i ++ )
321 ASSERTC(base + i, <, height * Term->TextWidth);
322 buffer[ base + i ].Ch = 0;
323 buffer[ base + i ].Colour = Term->CurColour;
330 * \brief Update the screen mode
331 * \param Term Terminal to update
332 * \param NewMode New mode to set
333 * \param NewWidth New framebuffer width
334 * \param NewHeight New framebuffer height
336 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
338 int oldW = Term->Width;
339 int oldTW = Term->TextWidth;
340 int oldH = Term->Height;
341 int oldTH = Term->TextHeight;
342 tVT_Char *oldTBuf = Term->Text;
343 Uint32 *oldFB = Term->Buffer;
348 // TODO: Increase RealWidth/RealHeight when this happens
349 if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth;
350 if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
352 // Fast exit if no resolution change
353 if(NewWidth == Term->Width && NewHeight == Term->Height)
356 // Calculate new dimensions
357 Term->Width = NewWidth;
358 Term->Height = NewHeight;
359 Term->TextWidth = NewWidth / giVT_CharWidth;
360 Term->TextHeight = NewHeight / giVT_CharHeight;
361 Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
363 // Allocate new buffers
366 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
371 w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
372 h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
373 h *= giVT_Scrollback + 1;
374 for( int i = 0; i < h; i ++ )
377 &Term->Text[i*Term->TextWidth],
386 Term->AltBuf = realloc(
388 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
393 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
395 w = (oldW > Term->Width) ? Term->Width : oldW;
396 h = (oldH > Term->Height) ? Term->Height : oldH;
397 for( int i = 0; i < h; i ++ )
400 &Term->Buffer[i*Term->Width],
413 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
414 Term, Term->TextWidth, Term->TextHeight);
417 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
418 Term, Term->Width, Term->Height);
420 //case TERM_MODE_2DACCEL:
421 //case TERM_MODE_3DACCEL:
430 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
433 Term->Flags |= VT_FLAG_ALTBUF;
435 Term->Flags &= ~VT_FLAG_ALTBUF;
436 VT_int_UpdateScreen(Term, 1);