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

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