Kernel/VTerm - Switched to using userland VT100 code
[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   1
9 #include "vterm.h"
10
11 extern int      Term_HandleVT100(tVTerm *Term, int Len, const char *Buf);
12
13 // === CODE ===
14
15 /**
16  * \fn void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
17  * \brief Print a string to the Virtual Terminal
18  */
19 void VT_int_PutString(tVTerm *Term, const Uint8 *Buffer, Uint Count)
20 {
21         // Iterate
22         for( int ofs = 0; ofs < Count; )
23         {
24                 int esc_len = Term_HandleVT100(Term, Count - ofs, (const void*)(Buffer + ofs));
25                 if( esc_len < 0 ) {
26                         esc_len = -esc_len;
27                         VT_int_PutRawString(Term, Buffer + ofs, esc_len);
28                         //Debug("Raw string '%.*s'", esc_len, Buffer+ofs);
29                 }
30                 else {
31                         //Debug("Escape code '%.*s'", esc_len, Buffer+ofs);
32                 }
33                 ASSERTCR(esc_len, >, 0, );
34                 ofs += esc_len;
35         }
36         // Update Screen
37         VT_int_UpdateScreen( Term, 1 );
38 }
39
40 void VT_int_PutRawString(tVTerm *Term, const Uint8 *String, size_t Bytes)
41 {
42         for( int i = 0; i < Bytes; ) {
43                 Uint32  val;
44                 i += ReadUTF8(String+i, &val);
45                 VT_int_PutChar(Term, val);
46         }
47 }
48
49 /**
50  * \fn void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
51  * \brief Write a single character to a VTerm
52  */
53 void VT_int_PutChar(tVTerm *Term, Uint32 Ch)
54 {
55          int    i;
56         tVT_Char        *buffer;
57          int    write_pos;
58          int    limit;
59         
60         if(Term->Flags & VT_FLAG_ALTBUF) {
61                 buffer = Term->AltBuf;
62                 write_pos = Term->AltWritePos;
63                 limit = Term->TextHeight * Term->TextWidth;
64         }
65         else {
66                 buffer = Term->Text;
67                 write_pos = Term->WritePos;
68                 limit = Term->TextHeight*(giVT_Scrollback+1) * Term->TextWidth;
69         }
70         
71         switch(Ch)
72         {
73         case '\0':      return; // Ignore NULL byte
74         case '\n':
75                 VT_int_UpdateScreen( Term, 0 ); // Update the line before newlining
76                 write_pos += Term->TextWidth;
77         case '\r':
78                 write_pos -= write_pos % Term->TextWidth;
79                 break;
80         
81         case '\t': {
82                 int line = write_pos / Term->TextWidth;
83                 write_pos %= Term->TextWidth;
84                 do {
85                         buffer[ write_pos ].Ch = '\0';
86                         buffer[ write_pos ].Colour = Term->CurColour;
87                         write_pos ++;
88                 } while(write_pos & 7);
89                 write_pos += line * Term->TextWidth;
90                 break; }
91         
92         case '\b':
93                 // Backspace is invalid at Offset 0
94                 if(write_pos == 0)      break;
95                 
96                 write_pos --;
97                 // Single Character
98                 if(buffer[ write_pos ].Ch != '\0') {
99                         buffer[ write_pos ].Ch = 0;
100                         buffer[ write_pos ].Colour = Term->CurColour;
101                         break;
102                 }
103                 // Tab
104                 i = 7;  // Limit it to 8
105                 do {
106                         buffer[ write_pos ].Ch = 0;
107                         buffer[ write_pos ].Colour = Term->CurColour;
108                         write_pos --;
109                 } while(write_pos && i-- && buffer[ write_pos ].Ch == '\0');
110                 if(buffer[ write_pos ].Ch != '\0')
111                         write_pos ++;
112                 break;
113         
114         default:
115                 ASSERTC(write_pos,<,limit);
116                 buffer[ write_pos ].Ch = Ch;
117                 buffer[ write_pos ].Colour = Term->CurColour;
118                 // Update the line before wrapping
119                 if( (write_pos + 1) % Term->TextWidth == 0 )
120                         VT_int_UpdateScreen( Term, 0 );
121                 write_pos ++;
122                 break;
123         }
124         
125         if(Term->Flags & VT_FLAG_ALTBUF)
126         {
127                 Term->AltWritePos = write_pos;
128                 
129                 if(Term->AltWritePos >= limit)
130                 {
131                         Term->AltWritePos -= Term->TextWidth;
132                         VT_int_ScrollText(Term, 1);
133                 }
134         }
135         else
136         {
137                 Term->WritePos = write_pos;
138                 // Move Screen
139                 // - Check if we need to scroll the entire scrollback buffer
140                 if(Term->WritePos >= limit)
141                 {
142                          int    base;
143                         
144                         // Update view position
145                         base = Term->TextWidth*Term->TextHeight*(giVT_Scrollback);
146                         if(Term->ViewPos < base)
147                                 Term->ViewPos += Term->Width;
148                         if(Term->ViewPos > base)
149                                 Term->ViewPos = base;
150                         
151                         VT_int_ScrollText(Term, 1);
152                         Term->WritePos -= Term->TextWidth;
153                 }
154                 // Ok, so we only need to scroll the screen
155                 else if(Term->WritePos >= Term->ViewPos + Term->TextWidth*Term->TextHeight)
156                 {
157                         VT_int_ScrollFramebuffer( Term, 1 );
158                         
159                         Term->ViewPos += Term->TextWidth;
160                 }
161         }
162         
163         //LEAVE('-');
164 }
165
166 void VT_int_ScrollText(tVTerm *Term, int Count)
167 {
168         tVT_Char        *buf;
169          int    height, init_write_pos;
170          int    len, i;
171          int    scroll_top, scroll_height;
172
173         // Get buffer pointer and attributes    
174         if( Term->Flags & VT_FLAG_ALTBUF )
175         {
176                 buf = Term->AltBuf;
177                 height = Term->TextHeight;
178                 init_write_pos = Term->AltWritePos;
179                 scroll_top = Term->ScrollTop;
180                 scroll_height = Term->ScrollHeight;
181         }
182         else
183         {
184                 buf = Term->Text;
185                 height = Term->TextHeight*(giVT_Scrollback+1);
186                 init_write_pos = Term->WritePos;
187                 scroll_top = 0;
188                 scroll_height = height;
189         }
190
191         // Scroll text upwards (more space at bottom)
192         if( Count > 0 )
193         {
194                  int    base;
195         
196                 // Set up
197                 if(Count > scroll_height)       Count = scroll_height;
198                 base = Term->TextWidth*(scroll_top + scroll_height - Count);
199                 len = Term->TextWidth*(scroll_height - Count);
200                 
201                 // Scroll terminal cache
202                 memmove(
203                         &buf[Term->TextWidth*scroll_top],
204                         &buf[Term->TextWidth*(scroll_top+Count)],
205                         len*sizeof(tVT_Char)
206                         );
207                 // Clear last rows
208                 for( i = 0; i < Term->TextWidth*Count; i ++ )
209                 {
210                         buf[ base + i ].Ch = 0;
211                         buf[ base + i ].Colour = Term->CurColour;
212                 }
213                 
214                 // Update Screen
215                 VT_int_ScrollFramebuffer( Term, Count );
216                 if( Term->Flags & VT_FLAG_ALTBUF )
217                         Term->AltWritePos = base;
218                 else
219                         Term->WritePos = Term->ViewPos + Term->TextWidth*(Term->TextHeight - Count);
220                 for( i = 0; i < Count; i ++ )
221                 {
222                         VT_int_UpdateScreen( Term, 0 );
223                         if( Term->Flags & VT_FLAG_ALTBUF )
224                                 Term->AltWritePos += Term->TextWidth;
225                         else
226                                 Term->WritePos += Term->TextWidth;
227                 }
228         }
229         else
230         {
231                 Count = -Count;
232                 if(Count > scroll_height)       Count = scroll_height;
233                 
234                 len = Term->TextWidth*(scroll_height - Count);
235                 
236                 // Scroll terminal cache
237                 memmove(
238                         &buf[Term->TextWidth*(scroll_top+Count)],
239                         &buf[Term->TextWidth*scroll_top],
240                         len*sizeof(tVT_Char)
241                         );
242                 // Clear preceding rows
243                 for( i = 0; i < Term->TextWidth*Count; i ++ )
244                 {
245                         buf[ i ].Ch = 0;
246                         buf[ i ].Colour = Term->CurColour;
247                 }
248                 
249                 VT_int_ScrollFramebuffer( Term, -Count );
250                 if( Term->Flags & VT_FLAG_ALTBUF )
251                         Term->AltWritePos = Term->TextWidth*scroll_top;
252                 else
253                         Term->WritePos = Term->ViewPos;
254                 for( i = 0; i < Count; i ++ )
255                 {
256                         VT_int_UpdateScreen( Term, 0 );
257                         if( Term->Flags & VT_FLAG_ALTBUF )
258                                 Term->AltWritePos += Term->TextWidth;
259                         else
260                                 Term->WritePos += Term->TextWidth;
261                 }
262         }
263         
264         if( Term->Flags & VT_FLAG_ALTBUF )
265                 Term->AltWritePos = init_write_pos;
266         else
267                 Term->WritePos = init_write_pos;
268 }
269
270 /**
271  * \brief Clears a line in a virtual terminal
272  * \param Term  Terminal to modify
273  * \param Num   Line number to clear
274  */
275 void VT_int_ClearLine(tVTerm *Term, int Num)
276 {
277          int    limit = Term->TextHeight * (Term->Flags & VT_FLAG_ALTBUF ? 1 : giVT_Scrollback + 1);
278         tVT_Char        *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
279         if( Num < 0 || Num >= limit )   return ;
280         
281         tVT_Char        *cell = &buffer[ Num*Term->TextWidth ];
282         
283         for( int i = 0; i < Term->TextWidth; i ++ )
284         {
285                 cell[ i ].Ch = 0;
286                 cell[ i ].Colour = Term->CurColour;
287         }
288 }
289
290 /**
291  * \brief Update the screen mode
292  * \param Term  Terminal to update
293  * \param NewMode       New mode to set
294  * \param NewWidth      New framebuffer width
295  * \param NewHeight     New framebuffer height
296  */
297 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
298 {
299          int    oldW = Term->Width;
300          int    oldTW = Term->TextWidth;
301          int    oldH = Term->Height;
302          int    oldTH = Term->TextHeight;
303         tVT_Char        *oldTBuf = Term->Text;
304         Uint32  *oldFB = Term->Buffer;
305          int    w, h, i;
306         
307         // TODO: Increase RealWidth/RealHeight when this happens
308         if(NewWidth > giVT_RealWidth)   NewWidth = giVT_RealWidth;
309         if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
310         
311         // Fast exit if no resolution change
312         if(NewWidth == Term->Width && NewHeight == Term->Height)
313                 return ;
314         
315         // Calculate new dimensions
316         Term->Width = NewWidth;
317         Term->Height = NewHeight;
318         Term->TextWidth = NewWidth / giVT_CharWidth;
319         Term->TextHeight = NewHeight / giVT_CharHeight;
320         Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
321         
322         // Allocate new buffers
323         // - Text
324         Term->Text = calloc(
325                 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
326                 sizeof(tVT_Char)
327                 );
328         if(oldTBuf) {
329                 // Copy old buffer
330                 w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
331                 h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
332                 h *= giVT_Scrollback + 1;
333                 for( i = 0; i < h; i ++ )
334                 {
335                         memcpy(
336                                 &Term->Text[i*Term->TextWidth],
337                                 &oldTBuf[i*oldTW],
338                                 w*sizeof(tVT_Char)
339                                 );      
340                 }
341                 free(oldTBuf);
342         }
343         
344         // - Alternate Text
345         Term->AltBuf = realloc(
346                 Term->AltBuf,
347                 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
348                 );
349         
350         // - Framebuffer
351         if(oldFB) {
352                 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
353                 // Copy old buffer
354                 w = (oldW > Term->Width) ? Term->Width : oldW;
355                 h = (oldH > Term->Height) ? Term->Height : oldH;
356                 for( i = 0; i < h; i ++ )
357                 {
358                         memcpy(
359                                 &Term->Buffer[i*Term->Width],
360                                 &oldFB[i*oldW],
361                                 w*sizeof(Uint32)
362                                 );
363                 }
364                 free(oldFB);
365         }
366         
367         // Debug
368         #if 0
369         switch(Term->Mode)
370         {
371         case TERM_MODE_TEXT:
372                 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
373                         Term, Term->TextWidth, Term->TextHeight);
374                 break;
375         case TERM_MODE_FB:
376                 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
377                         Term, Term->Width, Term->Height);
378                 break;
379         //case TERM_MODE_2DACCEL:
380         //case TERM_MODE_3DACCEL:
381         //      return;
382         }
383         #endif
384 }
385
386
387 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
388 {       
389         if(Enabled)
390                 Term->Flags |= VT_FLAG_ALTBUF;
391         else
392                 Term->Flags &= ~VT_FLAG_ALTBUF;
393         VT_int_UpdateScreen(Term, 1);
394 }
395

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