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

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