d4ea9a41a787cd307cd64956f7eaf036247b74e0
[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         
66         HEAP_VALIDATE();
67         
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);
71
72         ASSERTC(write_pos, >=, 0);
73
74         // Scroll entire buffer (about to write outside limit)
75         if( write_pos >= limit )
76         {
77                 ASSERTC(write_pos, <, limit + Term->TextWidth);
78                 VT_int_ScrollText(Term, 1);
79                 write_pos -= Term->TextWidth;
80         }
81
82         // Bring written cell into view
83         if( !(Term->Flags & VT_FLAG_ALTBUF) )
84         {
85                 size_t  onescreen = Term->TextWidth*Term->TextHeight;
86                 if( write_pos >= Term->ViewPos + onescreen )
87                 {
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;
93                 }
94                 else if( write_pos < Term->ViewPos )
95                 {
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;
101                 }
102                 else
103                 {
104                         // no action, cell is visible
105                 }
106         }
107         
108         switch(Ch)
109         {
110         case '\0':      // Ignore NULL byte
111                 return;
112         case '\n':
113                 VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
114                 write_pos += Term->TextWidth;
115                 // TODO: Scroll display down if needed
116         case '\r':
117                 write_pos -= write_pos % Term->TextWidth;
118                 break;
119         
120         case '\t': {
121                 size_t  col =  write_pos % Term->TextWidth;
122                 do {
123                         buffer[ write_pos ].Ch = '\0';
124                         buffer[ write_pos ].Colour = Term->CurColour;
125                         write_pos ++;
126                         col ++;
127                 } while( (col & 7) && col < Term->TextWidth );
128                 break; }
129         
130         case '\b':
131                 // Backspace is invalid at Offset 0
132                 if(write_pos == 0)      break;
133                 
134                 write_pos --;
135                 // Single Character
136                 if(buffer[ write_pos ].Ch != '\0') {
137                         buffer[ write_pos ].Ch = 0;
138                         buffer[ write_pos ].Colour = Term->CurColour;
139                         break;
140                 }
141                 // Tab
142                 i = 7;  // Limit it to 8
143                 do {
144                         buffer[ write_pos ].Ch = 0;
145                         buffer[ write_pos ].Colour = Term->CurColour;
146                         write_pos --;
147                 } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
148                 if(buffer[ write_pos ].Ch != '\0')
149                         write_pos ++;
150                 break;
151         
152         default:
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 );
158                 write_pos ++;
159                 break;
160         }
161         
162         ASSERTC(write_pos, <=, limit);
163         *VT_int_GetWritePosPtr(Term) = write_pos;
164
165         HEAP_VALIDATE();
166         
167         //LEAVE('-');
168 }
169
170 void VT_int_ScrollText(tVTerm *Term, int Count)
171 {
172         tVT_Char        *buf;
173          int    scroll_top, scroll_height;
174         
175         HEAP_VALIDATE();
176
177         // Get buffer pointer and attributes    
178         size_t  height = VT_int_GetBufferRows(Term);
179         size_t  *write_pos_ptr = VT_int_GetWritePosPtr(Term);
180         
181         if( Term->Flags & VT_FLAG_ALTBUF )
182         {
183                 buf = Term->AltBuf;
184                 scroll_top = Term->ScrollTop;
185                 scroll_height = Term->ScrollHeight;
186         }
187         else
188         {
189                 buf = Term->Text;
190                 scroll_top = 0;
191                 scroll_height = height;
192         }
193         
194         const int init_write_pos = *write_pos_ptr;
195
196         // Scroll text upwards (more space at bottom)
197         if( Count > 0 )
198         {
199                 // Set up
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);
204                 
205                 // Scroll terminal cache
206                 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
207                 memmove( &buf[base], &buf[base+chars], len*sizeof(tVT_Char) );
208                 
209                 // Clear last rows
210                 for( int i = 0; i < chars; i ++ )
211                 {
212                         ASSERTC(base + len + i, <, Term->TextWidth*height);
213                         buf[ base + len + i ].Ch = 0;
214                         buf[ base + len + i ].Colour = Term->CurColour;
215                 }
216                 
217                 // Update Screen
218                 VT_int_ScrollFramebuffer( Term, Count );
219                 if( Term->Flags & VT_FLAG_ALTBUF )
220                         Term->AltWritePos = base;
221                 else
222                         Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
223                 for( int i = 0; i < Count; i ++ )
224                 {
225                         VT_int_UpdateScreen( Term, 0 );
226                         *write_pos_ptr += Term->TextWidth;
227                 }
228         }
229         else
230         {
231                 Count = -Count;
232                 if(Count > scroll_height)       Count = scroll_height;
233                 
234                 size_t  chars = Term->TextWidth*Count;
235                 size_t  base = Term->TextWidth*scroll_top;
236                 size_t  len = Term->TextWidth*scroll_height - chars;
237                 
238                 // Scroll terminal cache
239                 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
240                 memmove( &buf[base+chars], &buf[base], len*sizeof(tVT_Char) );
241
242                 // Clear preceding rows
243                 for( int i = 0; i < chars; i ++ )
244                 {
245                         buf[ i ].Ch = 0;
246                         buf[ i ].Colour = Term->CurColour;
247                 }
248                 
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;
253                 else
254                         Term->WritePos = Term->ViewPos;
255                 for( int i = 0; i < Count; i ++ )
256                 {
257                         VT_int_UpdateScreen( Term, 0 );
258                 }
259         }
260         
261         *write_pos_ptr = init_write_pos;
262
263         HEAP_VALIDATE();
264 }
265
266 /**
267  * \brief Clears a line in a virtual terminal
268  * \param Term  Terminal to modify
269  * \param Num   Line number to clear
270  */
271 void VT_int_ClearLine(tVTerm *Term, int Row)
272 {
273         HEAP_VALIDATE();
274         
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, );
279         
280         size_t  base = Row * Term->TextWidth;
281         
282         for( int i = 0; i < Term->TextWidth; i ++ )
283         {
284                 buffer[ base + i ].Ch = 0;
285                 buffer[ base + i ].Colour = Term->CurColour;
286         }
287         
288         HEAP_VALIDATE();
289 }
290
291 void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
292 {
293         HEAP_VALIDATE();
294         
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, );
299
300         ASSERTCR(FirstCol, <=, LastCol, );
301         ASSERTCR(FirstCol, <, Term->TextWidth, );
302         ASSERTCR(LastCol, <=, Term->TextWidth, );
303         
304         size_t  base = Row * Term->TextWidth;
305         for( int i = FirstCol; i < LastCol; i ++ )
306         {
307                 ASSERTC(base + i, <, height * Term->TextWidth);
308                 buffer[ base + i ].Ch = 0;
309                 buffer[ base + i ].Colour = Term->CurColour;
310         }
311         
312         HEAP_VALIDATE();
313 }
314
315 /**
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
321  */
322 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
323 {
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;
330          int    w, h;
331         
332         HEAP_VALIDATE();
333         
334         // TODO: Increase RealWidth/RealHeight when this happens
335         if(NewWidth > giVT_RealWidth)   NewWidth = giVT_RealWidth;
336         if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
337         
338         // Fast exit if no resolution change
339         if(NewWidth == Term->Width && NewHeight == Term->Height)
340                 return ;
341         
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;
348         
349         // Allocate new buffers
350         // - Text
351         Term->Text = calloc(
352                 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
353                 sizeof(tVT_Char)
354                 );
355         if(oldTBuf) {
356                 // Copy old buffer
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 ++ )
361                 {
362                         memcpy(
363                                 &Term->Text[i*Term->TextWidth],
364                                 &oldTBuf[i*oldTW],
365                                 w*sizeof(tVT_Char)
366                                 );      
367                 }
368                 free(oldTBuf);
369         }
370         
371         // - Alternate Text
372         Term->AltBuf = realloc(
373                 Term->AltBuf,
374                 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
375                 );
376         
377         // - Framebuffer
378         if(oldFB) {
379                 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
380                 // Copy old buffer
381                 w = (oldW > Term->Width) ? Term->Width : oldW;
382                 h = (oldH > Term->Height) ? Term->Height : oldH;
383                 for( int i = 0; i < h; i ++ )
384                 {
385                         memcpy(
386                                 &Term->Buffer[i*Term->Width],
387                                 &oldFB[i*oldW],
388                                 w*sizeof(Uint32)
389                                 );
390                 }
391                 free(oldFB);
392         }
393         
394         // Debug
395         #if 0
396         switch(Term->Mode)
397         {
398         case TERM_MODE_TEXT:
399                 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
400                         Term, Term->TextWidth, Term->TextHeight);
401                 break;
402         case TERM_MODE_FB:
403                 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
404                         Term, Term->Width, Term->Height);
405                 break;
406         //case TERM_MODE_2DACCEL:
407         //case TERM_MODE_3DACCEL:
408         //      return;
409         }
410         #endif
411
412         HEAP_VALIDATE();
413 }
414
415
416 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
417 {       
418         if(Enabled)
419                 Term->Flags |= VT_FLAG_ALTBUF;
420         else
421                 Term->Flags &= ~VT_FLAG_ALTBUF;
422         VT_int_UpdateScreen(Term, 1);
423 }
424
425 size_t *VT_int_GetWritePosPtr(tVTerm *Term)
426 {
427         return ((Term->Flags & VT_FLAG_ALTBUF) ? &Term->AltWritePos : &Term->WritePos);
428 }
429
430 size_t VT_int_GetBufferRows(tVTerm *Term)
431 {
432         return ((Term->Flags & VT_FLAG_ALTBUF) ? 1 : (giVT_Scrollback+1))*Term->TextHeight;
433 }
434

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