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

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