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

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