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

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