Kernel/VTerm - "Fix" wrapping issue in VTerm (why was old behavior there?)
[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 @ %i", write_pos);
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 @ %i", write_pos);
174                         VT_int_UpdateScreen( Term, 0 );
175                         // NOTE: Code at the top of PutChar handles the actual wrapping
176                 }
177                 wrpos->Col ++;
178                 write_pos ++;
179                 break;
180         }
181         
182         ASSERTC(write_pos, <=, limit);
183
184         HEAP_VALIDATE();
185         
186         // TODO: Schedule a delayed screen update
187 }
188
189 void VT_int_ScrollText(tVTerm *Term, int Count)
190 {
191         ENTER("pTerm iCount", Term, Count);
192         tVT_Char        *buf;
193          int    scroll_top, scroll_height;
194         
195         HEAP_VALIDATE();
196
197         // Get buffer pointer and attributes    
198         size_t  height = VT_int_GetBufferRows(Term);
199         tVT_Pos *wrpos = VT_int_GetWritePosPtr(Term);
200         
201         if( Term->Flags & VT_FLAG_ALTBUF )
202         {
203                 buf = Term->AltBuf;
204                 scroll_top = Term->ScrollTop;
205                 scroll_height = Term->ScrollHeight;
206         }
207         else
208         {
209                 buf = Term->Text;
210                 scroll_top = 0;
211                 scroll_height = height;
212         }
213         
214         const tVT_Pos init_wrpos = *wrpos;
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                         wrpos->Row = scroll_top;
241                 else
242                         wrpos->Row = Term->ViewTopRow + (Term->TextHeight - Count);
243                 for( int i = 0; i < Count; i ++ )
244                 {
245                         VT_int_UpdateScreen( Term, 0 );
246                         wrpos->Row ++;
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                         wrpos->Row = scroll_top;
273                 else
274                         wrpos->Row = Term->ViewTopRow;
275                 for( int i = 0; i < Count; i ++ )
276                 {
277                         VT_int_UpdateScreen( Term, 0 );
278                         // Increment?
279                 }
280         }
281         
282         *wrpos = init_wrpos;
283
284         HEAP_VALIDATE();
285         LEAVE('-');
286 }
287
288 /**
289  * \brief Clears a line in a virtual terminal
290  * \param Term  Terminal to modify
291  * \param Num   Line number to clear
292  */
293 void VT_int_ClearLine(tVTerm *Term, int Row)
294 {
295         HEAP_VALIDATE();
296         
297         size_t  height = VT_int_GetBufferRows(Term);
298         tVT_Char        *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
299         ASSERTCR(Row, >=, 0, );
300         ASSERTCR(Row, <, height, );
301         
302         size_t  base = Row * Term->TextWidth;
303         
304         for( int i = 0; i < Term->TextWidth; i ++ )
305         {
306                 buffer[ base + i ].Ch = 0;
307                 buffer[ base + i ].Colour = Term->CurColour;
308         }
309         
310         HEAP_VALIDATE();
311 }
312
313 void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
314 {
315         HEAP_VALIDATE();
316         
317         size_t  height = VT_int_GetBufferRows(Term);
318         tVT_Char        *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
319         ASSERTCR(Row, >=, 0, );
320         ASSERTCR(Row, <, height, );
321
322         ASSERTCR(FirstCol, <=, LastCol, );
323         ASSERTCR(FirstCol, <, Term->TextWidth, );
324         ASSERTCR(LastCol, <=, Term->TextWidth, );
325         
326         size_t  base = Row * Term->TextWidth;
327         for( int i = FirstCol; i < LastCol; i ++ )
328         {
329                 ASSERTC(base + i, <, height * Term->TextWidth);
330                 buffer[ base + i ].Ch = 0;
331                 buffer[ base + i ].Colour = Term->CurColour;
332         }
333         
334         HEAP_VALIDATE();
335 }
336
337 /**
338  * \brief Update the screen mode
339  * \param Term  Terminal to update
340  * \param NewMode       New mode to set
341  * \param NewWidth      New framebuffer width
342  * \param NewHeight     New framebuffer height
343  */
344 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
345 {
346          int    oldW = Term->Width;
347          int    oldTW = Term->TextWidth;
348          int    oldH = Term->Height;
349          int    oldTH = Term->TextHeight;
350         tVT_Char        *oldTBuf = Term->Text;
351         Uint32  *oldFB = Term->Buffer;
352          int    w, h;
353         
354         HEAP_VALIDATE();
355         
356         // TODO: Increase RealWidth/RealHeight when this happens
357         if(NewWidth > giVT_RealWidth)   NewWidth = giVT_RealWidth;
358         if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
359         
360         // Fast exit if no resolution change
361         if(NewWidth == Term->Width && NewHeight == Term->Height)
362                 return ;
363         
364         // Calculate new dimensions
365         Term->Width = NewWidth;
366         Term->Height = NewHeight;
367         Term->TextWidth = NewWidth / giVT_CharWidth;
368         Term->TextHeight = NewHeight / giVT_CharHeight;
369         Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
370         
371         // Allocate new buffers
372         // - Text
373         Term->Text = calloc(
374                 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
375                 sizeof(tVT_Char)
376                 );
377         if(oldTBuf) {
378                 // Copy old buffer
379                 w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
380                 h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
381                 h *= giVT_Scrollback + 1;
382                 for( int i = 0; i < h; i ++ )
383                 {
384                         memcpy(
385                                 &Term->Text[i*Term->TextWidth],
386                                 &oldTBuf[i*oldTW],
387                                 w*sizeof(tVT_Char)
388                                 );      
389                 }
390                 free(oldTBuf);
391         }
392         
393         // - Alternate Text
394         Term->AltBuf = realloc(
395                 Term->AltBuf,
396                 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
397                 );
398         
399         // - Framebuffer
400         if(oldFB) {
401                 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
402                 // Copy old buffer
403                 w = (oldW > Term->Width) ? Term->Width : oldW;
404                 h = (oldH > Term->Height) ? Term->Height : oldH;
405                 for( int i = 0; i < h; i ++ )
406                 {
407                         memcpy(
408                                 &Term->Buffer[i*Term->Width],
409                                 &oldFB[i*oldW],
410                                 w*sizeof(Uint32)
411                                 );
412                 }
413                 free(oldFB);
414         }
415         
416         // Debug
417         #if 0
418         switch(Term->Mode)
419         {
420         case TERM_MODE_TEXT:
421                 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
422                         Term, Term->TextWidth, Term->TextHeight);
423                 break;
424         case TERM_MODE_FB:
425                 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
426                         Term, Term->Width, Term->Height);
427                 break;
428         //case TERM_MODE_2DACCEL:
429         //case TERM_MODE_3DACCEL:
430         //      return;
431         }
432         #endif
433
434         HEAP_VALIDATE();
435 }
436
437
438 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
439 {       
440         if(Enabled)
441                 Term->Flags |= VT_FLAG_ALTBUF;
442         else
443                 Term->Flags &= ~VT_FLAG_ALTBUF;
444         VT_int_UpdateScreen(Term, 1);
445 }
446
447 tVT_Pos *VT_int_GetWritePosPtr(tVTerm *Term)
448 {
449         return ((Term->Flags & VT_FLAG_ALTBUF) ? &Term->AltWritePos : &Term->WritePos);
450 }
451
452 size_t VT_int_GetBufferRows(tVTerm *Term)
453 {
454         return ((Term->Flags & VT_FLAG_ALTBUF) ? 1 : (giVT_Scrollback+1))*Term->TextHeight;
455 }
456

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