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

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