Kernel/VTerm - Replace offset WritePos with Row,Col pair
[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   1
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         //LEAVE('-');
180 }
181
182 void VT_int_ScrollText(tVTerm *Term, int Count)
183 {
184         tVT_Char        *buf;
185          int    scroll_top, scroll_height;
186         
187         HEAP_VALIDATE();
188
189         // Get buffer pointer and attributes    
190         size_t  height = VT_int_GetBufferRows(Term);
191         tVT_Pos *wrpos = VT_int_GetWritePosPtr(Term);
192         
193         if( Term->Flags & VT_FLAG_ALTBUF )
194         {
195                 buf = Term->AltBuf;
196                 scroll_top = Term->ScrollTop;
197                 scroll_height = Term->ScrollHeight;
198         }
199         else
200         {
201                 buf = Term->Text;
202                 scroll_top = 0;
203                 scroll_height = height;
204         }
205         
206         const tVT_Pos init_wrpos = *wrpos;
207
208         // Scroll text upwards (more space at bottom)
209         if( Count > 0 )
210         {
211                 // Set up
212                 if(Count > scroll_height)       Count = scroll_height;
213                 size_t  chars = Term->TextWidth*Count;
214                 size_t  base = Term->TextWidth*scroll_top;
215                 size_t  len = Term->TextWidth*(scroll_height - Count);
216                 
217                 // Scroll terminal cache
218                 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
219                 memmove( &buf[base], &buf[base+chars], len*sizeof(tVT_Char) );
220                 
221                 // Clear last rows
222                 for( int i = 0; i < chars; i ++ )
223                 {
224                         ASSERTC(base + len + i, <, Term->TextWidth*height);
225                         buf[ base + len + i ].Ch = 0;
226                         buf[ base + len + i ].Colour = Term->CurColour;
227                 }
228                 
229                 // Update Screen
230                 VT_int_ScrollFramebuffer( Term, Count );
231                 if( Term->Flags & VT_FLAG_ALTBUF )
232                         wrpos->Row = scroll_top;
233                 else
234                         wrpos->Row = Term->ViewTopRow + (Term->TextHeight - Count);
235                 for( int i = 0; i < Count; i ++ )
236                 {
237                         VT_int_UpdateScreen( Term, 0 );
238                         wrpos->Row ++;
239                 }
240         }
241         else
242         {
243                 Count = -Count;
244                 if(Count > scroll_height)       Count = scroll_height;
245                 
246                 size_t  chars = Term->TextWidth*Count;
247                 size_t  base = Term->TextWidth*scroll_top;
248                 size_t  len = Term->TextWidth*scroll_height - chars;
249                 
250                 // Scroll terminal cache
251                 ASSERTC( base + chars + len, <=, Term->TextWidth*height );
252                 memmove( &buf[base+chars], &buf[base], len*sizeof(tVT_Char) );
253
254                 // Clear preceding rows
255                 for( int i = 0; i < chars; i ++ )
256                 {
257                         buf[ i ].Ch = 0;
258                         buf[ i ].Colour = Term->CurColour;
259                 }
260                 
261                 // Update screen (shift framebuffer, re-render revealed lines)
262                 VT_int_ScrollFramebuffer( Term, -Count );
263                 if( Term->Flags & VT_FLAG_ALTBUF )
264                         wrpos->Row = scroll_top;
265                 else
266                         wrpos->Row = Term->ViewTopRow;
267                 for( int i = 0; i < Count; i ++ )
268                 {
269                         VT_int_UpdateScreen( Term, 0 );
270                         // Increment?
271                 }
272         }
273         
274         *wrpos = init_wrpos;
275
276         HEAP_VALIDATE();
277 }
278
279 /**
280  * \brief Clears a line in a virtual terminal
281  * \param Term  Terminal to modify
282  * \param Num   Line number to clear
283  */
284 void VT_int_ClearLine(tVTerm *Term, int Row)
285 {
286         HEAP_VALIDATE();
287         
288         size_t  height = VT_int_GetBufferRows(Term);
289         tVT_Char        *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
290         ASSERTCR(Row, >=, 0, );
291         ASSERTCR(Row, <, height, );
292         
293         size_t  base = Row * Term->TextWidth;
294         
295         for( int i = 0; i < Term->TextWidth; i ++ )
296         {
297                 buffer[ base + i ].Ch = 0;
298                 buffer[ base + i ].Colour = Term->CurColour;
299         }
300         
301         HEAP_VALIDATE();
302 }
303
304 void VT_int_ClearInLine(tVTerm *Term, int Row, int FirstCol, int LastCol)
305 {
306         HEAP_VALIDATE();
307         
308         size_t  height = VT_int_GetBufferRows(Term);
309         tVT_Char        *buffer = (Term->Flags & VT_FLAG_ALTBUF ? Term->AltBuf : Term->Text);
310         ASSERTCR(Row, >=, 0, );
311         ASSERTCR(Row, <, height, );
312
313         ASSERTCR(FirstCol, <=, LastCol, );
314         ASSERTCR(FirstCol, <, Term->TextWidth, );
315         ASSERTCR(LastCol, <=, Term->TextWidth, );
316         
317         size_t  base = Row * Term->TextWidth;
318         for( int i = FirstCol; i < LastCol; i ++ )
319         {
320                 ASSERTC(base + i, <, height * Term->TextWidth);
321                 buffer[ base + i ].Ch = 0;
322                 buffer[ base + i ].Colour = Term->CurColour;
323         }
324         
325         HEAP_VALIDATE();
326 }
327
328 /**
329  * \brief Update the screen mode
330  * \param Term  Terminal to update
331  * \param NewMode       New mode to set
332  * \param NewWidth      New framebuffer width
333  * \param NewHeight     New framebuffer height
334  */
335 void VT_int_Resize(tVTerm *Term, int NewWidth, int NewHeight)
336 {
337          int    oldW = Term->Width;
338          int    oldTW = Term->TextWidth;
339          int    oldH = Term->Height;
340          int    oldTH = Term->TextHeight;
341         tVT_Char        *oldTBuf = Term->Text;
342         Uint32  *oldFB = Term->Buffer;
343          int    w, h;
344         
345         HEAP_VALIDATE();
346         
347         // TODO: Increase RealWidth/RealHeight when this happens
348         if(NewWidth > giVT_RealWidth)   NewWidth = giVT_RealWidth;
349         if(NewHeight > giVT_RealHeight) NewHeight = giVT_RealHeight;
350         
351         // Fast exit if no resolution change
352         if(NewWidth == Term->Width && NewHeight == Term->Height)
353                 return ;
354         
355         // Calculate new dimensions
356         Term->Width = NewWidth;
357         Term->Height = NewHeight;
358         Term->TextWidth = NewWidth / giVT_CharWidth;
359         Term->TextHeight = NewHeight / giVT_CharHeight;
360         Term->ScrollHeight = Term->TextHeight - (oldTH - Term->ScrollHeight) - Term->ScrollTop;
361         
362         // Allocate new buffers
363         // - Text
364         Term->Text = calloc(
365                 Term->TextWidth * Term->TextHeight * (giVT_Scrollback+1),
366                 sizeof(tVT_Char)
367                 );
368         if(oldTBuf) {
369                 // Copy old buffer
370                 w = (oldTW > Term->TextWidth) ? Term->TextWidth : oldTW;
371                 h = (oldTH > Term->TextHeight) ? Term->TextHeight : oldTH;
372                 h *= giVT_Scrollback + 1;
373                 for( int i = 0; i < h; i ++ )
374                 {
375                         memcpy(
376                                 &Term->Text[i*Term->TextWidth],
377                                 &oldTBuf[i*oldTW],
378                                 w*sizeof(tVT_Char)
379                                 );      
380                 }
381                 free(oldTBuf);
382         }
383         
384         // - Alternate Text
385         Term->AltBuf = realloc(
386                 Term->AltBuf,
387                 Term->TextWidth * Term->TextHeight * sizeof(tVT_Char)
388                 );
389         
390         // - Framebuffer
391         if(oldFB) {
392                 Term->Buffer = calloc( Term->Width * Term->Height, sizeof(Uint32) );
393                 // Copy old buffer
394                 w = (oldW > Term->Width) ? Term->Width : oldW;
395                 h = (oldH > Term->Height) ? Term->Height : oldH;
396                 for( int i = 0; i < h; i ++ )
397                 {
398                         memcpy(
399                                 &Term->Buffer[i*Term->Width],
400                                 &oldFB[i*oldW],
401                                 w*sizeof(Uint32)
402                                 );
403                 }
404                 free(oldFB);
405         }
406         
407         // Debug
408         #if 0
409         switch(Term->Mode)
410         {
411         case TERM_MODE_TEXT:
412                 Log_Log("VTerm", "Set VT %p to text mode (%ix%i)",
413                         Term, Term->TextWidth, Term->TextHeight);
414                 break;
415         case TERM_MODE_FB:
416                 Log_Log("VTerm", "Set VT %p to framebuffer mode (%ix%i)",
417                         Term, Term->Width, Term->Height);
418                 break;
419         //case TERM_MODE_2DACCEL:
420         //case TERM_MODE_3DACCEL:
421         //      return;
422         }
423         #endif
424
425         HEAP_VALIDATE();
426 }
427
428
429 void VT_int_ToggleAltBuffer(tVTerm *Term, int Enabled)
430 {       
431         if(Enabled)
432                 Term->Flags |= VT_FLAG_ALTBUF;
433         else
434                 Term->Flags &= ~VT_FLAG_ALTBUF;
435         VT_int_UpdateScreen(Term, 1);
436 }
437
438 tVT_Pos *VT_int_GetWritePosPtr(tVTerm *Term)
439 {
440         return ((Term->Flags & VT_FLAG_ALTBUF) ? &Term->AltWritePos : &Term->WritePos);
441 }
442
443 size_t VT_int_GetBufferRows(tVTerm *Term)
444 {
445         return ((Term->Flags & VT_FLAG_ALTBUF) ? 1 : (giVT_Scrollback+1))*Term->TextHeight;
446 }
447

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