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)
68 size_t limit = VT_int_GetBufferRows(Term) * Term->TextWidth;
69 size_t write_pos = *VT_int_GetWritePosPtr(Term);
70 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
72 ASSERTC(write_pos, >=, 0);
74 // Scroll entire buffer (about to write outside limit)
75 if( write_pos >= limit )
77 ASSERTC(write_pos, <, limit + Term->TextWidth);
78 VT_int_ScrollText(Term, 1);
79 write_pos -= Term->TextWidth;
82 // Bring written cell into view
83 if( !(Term->Flags & VT_FLAG_ALTBUF) )
85 size_t onescreen = Term->TextWidth*Term->TextHeight;
86 if( write_pos >= Term->ViewPos + onescreen )
88 size_t new_pos = write_pos - (write_pos % Term->TextWidth) - onescreen + Term->TextWidth;
89 size_t count = (new_pos - Term->ViewPos) / Term->TextWidth;
90 VT_int_ScrollFramebuffer(Term, count);
91 //Debug("VT_int_PutChar: VScroll down to %i", new_pos/Term->TextWidth);
92 Term->ViewPos = new_pos;
94 else if( write_pos < Term->ViewPos )
96 size_t new_pos = write_pos - (write_pos % Term->TextWidth);
97 size_t count = (Term->ViewPos - new_pos) / Term->TextWidth;
98 VT_int_ScrollFramebuffer(Term, -count);
99 //Debug("VT_int_PutChar: VScroll up to %i", new_pos/Term->TextWidth);
100 Term->ViewPos = new_pos;
104 // no action, cell is visible
110 case '\0': // Ignore NULL byte
113 VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
114 write_pos += Term->TextWidth;
115 // TODO: Scroll display down if needed
117 write_pos -= write_pos % Term->TextWidth;
121 size_t col = write_pos % Term->TextWidth;
123 buffer[ write_pos ].Ch = '\0';
124 buffer[ write_pos ].Colour = Term->CurColour;
127 } while( (col & 7) && col < Term->TextWidth );
131 // Backspace is invalid at Offset 0
132 if(write_pos == 0) break;
136 if(buffer[ write_pos ].Ch != '\0') {
137 buffer[ write_pos ].Ch = 0;
138 buffer[ write_pos ].Colour = Term->CurColour;
142 i = 7; // Limit it to 8
144 buffer[ write_pos ].Ch = 0;
145 buffer[ write_pos ].Colour = Term->CurColour;
147 } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
148 if(buffer[ write_pos ].Ch != '\0')
153 buffer[ write_pos ].Ch = Ch;
154 buffer[ write_pos ].Colour = Term->CurColour;
155 // Update the line before wrapping
156 if( (write_pos + 1) % Term->TextWidth == 0 )
157 VT_int_UpdateScreen( Term, 0 );
162 ASSERTC(write_pos, <=, limit);
163 *VT_int_GetWritePosPtr(Term) = write_pos;
170 void VT_int_ScrollText(tVTerm *Term, int Count)
173 int scroll_top, scroll_height;
177 // Get buffer pointer and attributes
178 size_t height = VT_int_GetBufferRows(Term);
179 size_t *write_pos_ptr = VT_int_GetWritePosPtr(Term);
181 if( Term->Flags & VT_FLAG_ALTBUF )
184 scroll_top = Term->ScrollTop;
185 scroll_height = Term->ScrollHeight;
191 scroll_height = height;
194 const int init_write_pos = *write_pos_ptr;
196 // Scroll text upwards (more space at bottom)
200 if(Count > scroll_height) Count = scroll_height;
201 size_t chars = Term->TextWidth*Count;
202 size_t base = Term->TextWidth*scroll_top;
203 size_t len = Term->TextWidth*(scroll_height - Count);
205 // Scroll terminal cache
206 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
207 memmove( &buf[base], &buf[base+chars], len*sizeof(tVT_Char) );
210 for( int i = 0; i < chars; i ++ )
212 ASSERTC(base + len + i, <, Term->TextWidth*height);
213 buf[ base + len + i ].Ch = 0;
214 buf[ base + len + i ].Colour = Term->CurColour;
218 VT_int_ScrollFramebuffer( Term, Count );
219 if( Term->Flags & VT_FLAG_ALTBUF )
220 Term->AltWritePos = base;
222 Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
223 for( int i = 0; i < Count; i ++ )
225 VT_int_UpdateScreen( Term, 0 );
226 *write_pos_ptr += Term->TextWidth;
232 if(Count > scroll_height) Count = scroll_height;
234 size_t chars = Term->TextWidth*Count;
235 size_t base = Term->TextWidth*scroll_top;
236 size_t len = Term->TextWidth*scroll_height - chars;
238 // Scroll terminal cache
239 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
240 memmove( &buf[base+chars], &buf[base], len*sizeof(tVT_Char) );
242 // Clear preceding rows
243 for( int i = 0; i < chars; i ++ )
246 buf[ i ].Colour = Term->CurColour;
249 // Update screen (shift framebuffer, re-render revealed lines)
250 VT_int_ScrollFramebuffer( Term, -Count );
251 if( Term->Flags & VT_FLAG_ALTBUF )
252 Term->AltWritePos = Term->TextWidth*scroll_top;
254 Term->WritePos = Term->ViewPos;
255 for( int i = 0; i < Count; i ++ )
257 VT_int_UpdateScreen( Term, 0 );
261 *write_pos_ptr = init_write_pos;
267 * \brief Clears a line in a virtual terminal
268 * \param Term Terminal to modify
269 * \param Num Line number to clear
271 void VT_int_ClearLine(tVTerm *Term, int Row)
275 size_t height = VT_int_GetBufferRows(Term);
276 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
277 ASSERTCR(Row, >=, 0, );
278 ASSERTCR(Row, <, height, );
280 size_t base = Row * Term->TextWidth;
282 for( int i = 0; i < Term->TextWidth; i ++ )
284 buffer[ base + i ].Ch = 0;
285 buffer[ base + i ].Colour = Term->CurColour;
291 void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
295 size_t height = VT_int_GetBufferRows(Term);
296 tVT_Char *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
297 ASSERTCR(Row, >=, 0, );
298 ASSERTCR(Row, <, height, );
300 ASSERTCR(FirstCol, <=, LastCol, );
301 ASSERTCR(FirstCol, <, Term->TextWidth, );
302 ASSERTCR(LastCol, <=, Term->TextWidth, );
304 size_t base = Row * Term->TextWidth;
305 for( int i = FirstCol; i < LastCol; i ++ )
307 ASSERTC(base + i, <, height * Term->TextWidth);
308 buffer[ base + i ].Ch = 0;
309 buffer[ base + i ].Colour = Term->CurColour;
316 * \brief Update the screen mode
317 * \param Term Terminal to update
318 * \param NewMode New mode to set
319 * \param NewWidth New framebuffer width
320 * \param NewHeight New framebuffer height
322 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
324 int oldW = Term->Width;
325 int oldTW = Term->TextWidth;
326 int oldH = Term->Height;
327 int oldTH = Term->TextHeight;
328 tVT_Char *oldTBuf = Term->Text;
329 Uint32 *oldFB = Term->Buffer;
334 // TODO: Increase RealWidth/RealHeight when this happens
335 if(NewWidth > giVT_RealWidth) NewWidth = giVT_RealWidth;
336 if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
338 // Fast exit if no resolution change
339 if(NewWidth == Term->Width && NewHeight == Term->Height)
342 // Calculate new dimensions
343 Term->Width = NewWidth;
344 Term->Height = NewHeight;
345 Term->TextWidth = NewWidth / giVT_CharWidth;
346 Term->TextHeight = NewHeight / giVT_CharHeight;
347 Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
349 // Allocate new buffers
352 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
357 w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
358 h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
359 h *= giVT_Scrollback + 1;
360 for( int i = 0; i < h; i ++ )
363 &Term->Text[i*Term->TextWidth],
372 Term->AltBuf = realloc(
374 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
379 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
381 w = (oldW > Term->Width) ? Term->Width : oldW;
382 h = (oldH > Term->Height) ? Term->Height : oldH;
383 for( int i = 0; i < h; i ++ )
386 &Term->Buffer[i*Term->Width],
399 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
400 Term, Term->TextWidth, Term->TextHeight);
403 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
404 Term, Term->Width, Term->Height);
406 //case TERM_MODE_2DACCEL:
407 //case TERM_MODE_3DACCEL:
416 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
419 Term->Flags |= VT_FLAG_ALTBUF;
421 Term->Flags &= ~VT_FLAG_ALTBUF;
422 VT_int_UpdateScreen(Term, 1);
425 size_t *VT_int_GetWritePosPtr(tVTerm *Term)
427 return ((Term->Flags & VT_FLAG_ALTBUF) ? &Term->AltWritePos : &Term->WritePos);
430 size_t VT_int_GetBufferRows(tVTerm *Term)
432 return ((Term->Flags & VT_FLAG_ALTBUF) ? 1 : (giVT_Scrollback+1))*Term->TextHeight;