Kernel/vterm - Fix clobbering of AltBuf's heap footer
[tpg/acess2.git] / KernelLand / Kernel / drv / vterm_termbuf.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * drv/vterm_termbuf.c
6  * - Virtual Terminal - Terminal buffer manipulation
7  */
8 #define DEBUG   0
9 #define DEBUG_CHECKHEAP 0
10 #include "vterm.h"
11
12 #if DEBUG_CHECKHEAP
13 # define HEAP_VALIDATE()        Heap_Validate()
14 #else
15 # define HEAP_VALIDATE()        do{}while(0)
16 #endif
17
18 extern int      Term_HandleVT100(tVTerm *Term, int Len, const char *Buf);
19
20 // === CODE ===
21
22 /**
23  * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
24  * \brief Print a string to the Virtual Terminal
25  */
26 void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
27 {
28         // Iterate
29         for( int ofs = 0; ofs < Count; )
30         {
31                 int esc_len = Term_HandleVT100(Term, Count - ofs, (const void*)(Buffer + ofs));
32                 if( esc_len < 0 ) {
33                         esc_len = -esc_len;
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);
38                 }
39                 else {
40                         //Debug("Escape code '%.*s'", esc_len, Buffer+ofs);
41                 }
42                 ASSERTCR(esc_len, >, 0, );
43                 ofs += esc_len;
44         }
45         // Update Screen
46         VT_int_UpdateScreen( Term, 1 );
47 }
48
49 void VT_int_PutRawString(tVTerm *Term, const Uint8 *String, size_t Bytes)
50 {
51         for( int i = 0; i < Bytes; ) {
52                 Uint32  val;
53                 i += ReadUTF8(String+i, &val);
54                 VT_int_PutChar(Term, val);
55         }
56 }
57
58 /**
59  * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
60  * \brief Write a single character to a VTerm
61  */
62 void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
63 {
64          int    i;
65         tVT_Char        *buffer;
66          int    write_pos;
67          int    limit;
68         
69         HEAP_VALIDATE();
70         
71         if(Term->Flags & VT_FLAG_ALTBUF) {
72                 buffer = Term->AltBuf;
73                 write_pos = Term->AltWritePos;
74                 limit = Term->TextHeight * Term->TextWidth;
75         }
76         else {
77                 buffer = Term->Text;
78                 write_pos = Term->WritePos;
79                 limit = Term->TextHeight*(giVT_Scrollback+1) * Term->TextWidth;
80         }
81         
82         // TODO: Can the write position be equal to the end of screen?
83         ASSERTC(write_pos, <, limit);
84         
85         switch(Ch)
86         {
87         case '\0':      // Ignore NULL byte
88                 return;
89         case '\n':
90                 VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
91                 write_pos += Term->TextWidth;
92         case '\r':
93                 write_pos -= write_pos % Term->TextWidth;
94                 break;
95         
96         case '\t': {
97                 size_t  col =  write_pos % Term->TextWidth;
98                 do {
99                         buffer[ write_pos ].Ch = '\0';
100                         buffer[ write_pos ].Colour = Term->CurColour;
101                         write_pos ++;
102                         col ++;
103                 } while( (col & 7) && col < Term->TextWidth );
104                 break; }
105         
106         case '\b':
107                 // Backspace is invalid at Offset 0
108                 if(write_pos == 0)      break;
109                 
110                 write_pos --;
111                 // Single Character
112                 if(buffer[ write_pos ].Ch != '\0') {
113                         buffer[ write_pos ].Ch = 0;
114                         buffer[ write_pos ].Colour = Term->CurColour;
115                         break;
116                 }
117                 // Tab
118                 i = 7;  // Limit it to 8
119                 do {
120                         buffer[ write_pos ].Ch = 0;
121                         buffer[ write_pos ].Colour = Term->CurColour;
122                         write_pos --;
123                 } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
124                 if(buffer[ write_pos ].Ch != '\0')
125                         write_pos ++;
126                 break;
127         
128         default:
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 );
134                 write_pos ++;
135                 break;
136         }
137         
138         if(Term->Flags & VT_FLAG_ALTBUF)
139         {
140                 Term->AltWritePos = write_pos;
141                 
142                 if(Term->AltWritePos >= limit)
143                 {
144                         Term->AltWritePos -= Term->TextWidth;
145                         VT_int_ScrollText(Term, 1);
146                 }
147         }
148         else
149         {
150                 Term->WritePos = write_pos;
151                 // Move Screen
152                 // - Check if we need to scroll the entire scrollback buffer
153                 if(Term->WritePos >= limit)
154                 {
155                          int    base;
156                         
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;
163                         
164                         VT_int_ScrollText(Term, 1);
165                         Term->WritePos -= Term->TextWidth;
166                 }
167                 // Ok, so we only need to scroll the screen
168                 else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight)
169                 {
170                         VT_int_ScrollFramebuffer( Term, 1 );
171                         
172                         Term->ViewPos += Term->TextWidth;
173                 }
174         }
175
176         HEAP_VALIDATE();
177         
178         //LEAVE('-');
179 }
180
181 void VT_int_ScrollText(tVTerm *Term, int Count)
182 {
183         tVT_Char        *buf;
184          int    *write_pos_ptr;
185          int    height;
186          int    scroll_top, scroll_height;
187         
188         HEAP_VALIDATE();
189
190         // Get buffer pointer and attributes    
191         if( Term->Flags & VT_FLAG_ALTBUF )
192         {
193                 buf = Term->AltBuf;
194                 height = Term->TextHeight;
195                 write_pos_ptr = &Term->AltWritePos;
196                 scroll_top = Term->ScrollTop;
197                 scroll_height = Term->ScrollHeight;
198         }
199         else
200         {
201                 buf = Term->Text;
202                 height = Term->TextHeight*(giVT_Scrollback+1);
203                 write_pos_ptr = &Term->WritePos;
204                 scroll_top = 0;
205                 scroll_height = height;
206         }
207         
208         const int init_write_pos = *write_pos_ptr;
209
210         // Scroll text upwards (more space at bottom)
211         if( Count > 0 )
212         {
213                 // Set up
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);
218                 
219                 // Scroll terminal cache
220                 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
221                 memmove( &buf[base], &buf[base+chars], len*sizeof(tVT_Char) );
222                 
223                 // Clear last rows
224                 for( int i = 0; i < chars; i ++ )
225                 {
226                         ASSERTC(base + len + i, <, Term->TextWidth*height);
227                         buf[ base + len + i ].Ch = 0;
228                         buf[ base + len + i ].Colour = Term->CurColour;
229                 }
230                 
231                 // Update Screen
232                 VT_int_ScrollFramebuffer( Term, Count );
233                 if( Term->Flags & VT_FLAG_ALTBUF )
234                         Term->AltWritePos = base;
235                 else
236                         Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
237                 for( int i = 0; i < Count; i ++ )
238                 {
239                         VT_int_UpdateScreen( Term, 0 );
240                         *write_pos_ptr += Term->TextWidth;
241                 }
242         }
243         else
244         {
245                 Count = -Count;
246                 if(Count > scroll_height)       Count = scroll_height;
247                 
248                 size_t  chars = Term->TextWidth*Count;
249                 size_t  base = Term->TextWidth*scroll_top;
250                 size_t  len = Term->TextWidth*scroll_height - chars;
251                 
252                 // Scroll terminal cache
253                 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
254                 memmove( &buf[base+chars], &buf[base], len*sizeof(tVT_Char) );
255
256                 // Clear preceding rows
257                 for( int i = 0; i < chars; i ++ )
258                 {
259                         buf[ i ].Ch = 0;
260                         buf[ i ].Colour = Term->CurColour;
261                 }
262                 
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;
267                 else
268                         Term->WritePos = Term->ViewPos;
269                 for( int i = 0; i < Count; i ++ )
270                 {
271                         VT_int_UpdateScreen( Term, 0 );
272                 }
273         }
274         
275         *write_pos_ptr = init_write_pos;
276
277         HEAP_VALIDATE();
278 }
279
280 /**
281  * \brief Clears a line in a virtual terminal
282  * \param Term  Terminal to modify
283  * \param Num   Line number to clear
284  */
285 void VT_int_ClearLine(tVTerm *Term, int Row)
286 {
287         HEAP_VALIDATE();
288         
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, );
293         
294         size_t  base = Row * Term->TextWidth;
295         
296         for( int i = 0; i < Term->TextWidth; i ++ )
297         {
298                 buffer[ base + i ].Ch = 0;
299                 buffer[ base + i ].Colour = Term->CurColour;
300         }
301         
302         HEAP_VALIDATE();
303 }
304
305 void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
306 {
307         HEAP_VALIDATE();
308         
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, );
313
314         ASSERTCR(FirstCol, <=, LastCol, );
315         ASSERTCR(FirstCol, <, Term->TextWidth, );
316         ASSERTCR(LastCol, <=, Term->TextWidth, );
317         
318         size_t  base = Row * Term->TextWidth;
319         for( int i = FirstCol; i < LastCol; i ++ )
320         {
321                 ASSERTC(base + i, <, height * Term->TextWidth);
322                 buffer[ base + i ].Ch = 0;
323                 buffer[ base + i ].Colour = Term->CurColour;
324         }
325         
326         HEAP_VALIDATE();
327 }
328
329 /**
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
335  */
336 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
337 {
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;
344          int    w, h;
345         
346         HEAP_VALIDATE();
347         
348         // TODO: Increase RealWidth/RealHeight when this happens
349         if(NewWidth > giVT_RealWidth)   NewWidth = giVT_RealWidth;
350         if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
351         
352         // Fast exit if no resolution change
353         if(NewWidth == Term->Width && NewHeight == Term->Height)
354                 return ;
355         
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;
362         
363         // Allocate new buffers
364         // - Text
365         Term->Text = calloc(
366                 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
367                 sizeof(tVT_Char)
368                 );
369         if(oldTBuf) {
370                 // Copy old buffer
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 ++ )
375                 {
376                         memcpy(
377                                 &Term->Text[i*Term->TextWidth],
378                                 &oldTBuf[i*oldTW],
379                                 w*sizeof(tVT_Char)
380                                 );      
381                 }
382                 free(oldTBuf);
383         }
384         
385         // - Alternate Text
386         Term->AltBuf = realloc(
387                 Term->AltBuf,
388                 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
389                 );
390         
391         // - Framebuffer
392         if(oldFB) {
393                 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
394                 // Copy old buffer
395                 w = (oldW > Term->Width) ? Term->Width : oldW;
396                 h = (oldH > Term->Height) ? Term->Height : oldH;
397                 for( int i = 0; i < h; i ++ )
398                 {
399                         memcpy(
400                                 &Term->Buffer[i*Term->Width],
401                                 &oldFB[i*oldW],
402                                 w*sizeof(Uint32)
403                                 );
404                 }
405                 free(oldFB);
406         }
407         
408         // Debug
409         #if 0
410         switch(Term->Mode)
411         {
412         case TERM_MODE_TEXT:
413                 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
414                         Term, Term->TextWidth, Term->TextHeight);
415                 break;
416         case TERM_MODE_FB:
417                 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
418                         Term, Term->Width, Term->Height);
419                 break;
420         //case TERM_MODE_2DACCEL:
421         //case TERM_MODE_3DACCEL:
422         //      return;
423         }
424         #endif
425
426         HEAP_VALIDATE();
427 }
428
429
430 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
431 {       
432         if(Enabled)
433                 Term->Flags |= VT_FLAG_ALTBUF;
434         else
435                 Term->Flags &= ~VT_FLAG_ALTBUF;
436         VT_int_UpdateScreen(Term, 1);
437 }
438

UCC git Repository :: git.ucc.asn.au