Kernel/VTerm - Code cleanup and VT100 bugfixes
[tpg/acess2.git] / Usermode / Applications / gui_terminal_src / vt100.c
1 /*
2  * Acess GUI Terminal
3  * - By John Hodge (thePowersGang)
4  *
5  * vt100.c
6  * - VT100/xterm Emulation
7  */
8 #include <limits.h>
9 #include "include/vt100.h"
10 #include "include/display.h"
11 #include <ctype.h>      // isalpha
12 #ifdef KERNEL_VERSION
13 # define _SysDebug(v...)        Debug("VT100 "v)
14 #else
15 # include <acess/sys.h> // _SysDebug
16 # include <string.h>
17 # include <assert.h>
18 # include <stdlib.h>    // malloc/free
19
20 static inline int MIN(int a, int b)
21 {
22         return a < b ? a : b;
23 }
24 #endif
25
26 enum eExcapeMode {
27         MODE_NORMAL,
28         MODE_IGNORE,
29         MODE_STRING
30 };
31 enum eStringType {
32         STRING_IGNORE,
33         STRING_TITLE
34 };
35
36 #define FLAG_BOLD       0x01
37 #define FLAG_REVERSE    0x02
38
39 #define MAX_VT100_ESCAPE_LEN    32
40 typedef struct {
41         uint32_t        Flags;
42         int     CurFG, CurBG;
43
44         char    cache[MAX_VT100_ESCAPE_LEN];
45          int    cache_len;      
46
47         enum eExcapeMode        Mode;
48         
49         enum eStringType        StringType;
50         size_t  StringLen;
51         char    *StringCache;
52 } tVT100State;
53
54 const uint32_t  caVT100Colours[] = {
55         // Black,      Red,    Green,   Yellow,     Blue,  Magenta,     Cyan,      Gray
56         // Same again, but bright
57         0x000000, 0x770000, 0x007700, 0x777700, 0x000077, 0x770077, 0x007777, 0xAAAAAA,
58         0xCCCCCC, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF, 
59 };
60
61  int    _locate_eos(size_t Len, const char *Buf);
62  int    Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf);
63  int    Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf);
64  int    Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf);
65
66 int _locate_eos(size_t Len, const char *Buf)
67 {
68         for( size_t ret = 0; ret < Len; ret ++ )
69         {
70                 if( Buf[ret] == '\007' )
71                         return ret;
72                 if( Buf[ret] == '\x9c' )
73                         return ret;
74                 if( ret+1 < Len && Buf[ret] == '\x1b' && Buf[ret+1] == '\\' )
75                         return ret;
76         }
77         return -1;
78 }
79
80 int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
81 {
82         tVT100State     *st = Display_GetTermState(Term);
83         
84         if( st == NULL ) {
85                 st = malloc( sizeof(*st) );
86                 memset(st, 0, sizeof(*st));
87                 Display_SetTermState(Term, st);
88         }
89
90         if( st->Mode == MODE_IGNORE ) {
91                 st->Mode = MODE_NORMAL;
92                 // Used for multi-byte EOS
93                 _SysDebug("Ignore 1 '%c'", *Buf);
94                 return 1;
95         }
96         else if( st->Mode == MODE_STRING )
97         {
98                 // We're in a string mode
99                 int pos = _locate_eos(Len, Buf);
100                 size_t bytes = (pos >= 0 ? pos : Len);
101                 char *tmp = realloc(st->StringCache, st->StringLen + bytes+1);
102                 if(!tmp)        return bytes;
103                 st->StringCache = tmp;
104                 memcpy(tmp+st->StringLen, Buf, bytes);
105                 tmp[st->StringLen+bytes] = 0;
106                 st->StringLen += bytes;
107                 
108                 _SysDebug("pos=%i", pos);
109                 _SysDebug("Buf[+%zi] = '%.*s'", bytes, bytes, Buf);
110                 // Only apply when we hit EOS at the start
111                 if( pos != 0 )
112                         return bytes;
113                 switch(st->StringType)
114                 {
115                 case STRING_TITLE:
116                         Display_SetTitle(Term, st->StringCache);
117                         break;
118                 case STRING_IGNORE:
119                         break;
120                 }
121                 free(st->StringCache);
122                 st->StringCache = 0;
123                 st->StringLen = 0;
124                 
125                 if( *Buf == '\x1b' ) {
126                         st->Mode = MODE_IGNORE;
127                         // skip the '\\'
128                 }
129                 else
130                         st->Mode = MODE_NORMAL;
131                 return 1;
132         }
133         else {
134                 // fall through
135         }
136
137         if( st->cache_len > 0   || *Buf == '\x1b' )
138         {
139                 // Handle VT100 (like) escape sequence
140                  int    new_bytes = MIN(MAX_VT100_ESCAPE_LEN - st->cache_len, Len);
141                  int    ret = 0;
142                  int    old_inc_len = st->cache_len;
143                 
144                 memcpy(st->cache + st->cache_len, Buf, new_bytes);
145
146                 if( new_bytes == 0 ) {
147                         _SysDebug("Term_HandleVT100: Hit max? (Len=%i) Flushing cache", Len);
148                         st->cache_len = 0;
149                         return 0;
150                 }
151
152                 st->cache_len += new_bytes;
153                 assert(st->cache_len > old_inc_len);
154
155                 if( st->cache_len <= 1 )
156                         return 1;       // Skip 1 character (the '\x1b')
157
158                 ret = Term_HandleVT100_Short(Term, st->cache_len, st->cache);
159
160                 if( ret != 0 ) {
161                         // Check that we actually used the new data (as should have happened)
162                         if( ret <= old_inc_len ) {
163                                 _SysDebug("Term_HandleVT100: ret(%i) <= old_inc_len(%i), inc_len=%i, '%*C'",
164                                         ret, old_inc_len, st->cache_len, st->cache_len, st->cache);
165                                 assert(ret > old_inc_len);
166                         }
167                         st->cache_len = 0;
168                         //_SysDebug("%i bytes of escape code '%.*s' (return %i)",
169                         //      ret, ret, inc_buf, ret-old_inc_len);
170                         ret -= old_inc_len;     // counter cached bytes
171                 }
172                 else {
173                         ret = new_bytes;
174                         _SysDebug("Term_HandleVT100: Caching %i bytes '%*C'", ret, ret, st->cache);
175                 }
176                 return ret;
177         }
178
179         switch( *Buf )
180         {
181         case '\b':
182                 // backspace is aprarently just supposed to cursor left (if possible)
183                 Display_MoveCursor(Term, 0, -1);
184                 //Display_AddText(Term, 1, " ");
185                 //Display_MoveCursor(Term, 0, -1);
186                 return 1;
187         case '\t':
188                 // TODO: tab (get current cursor pos, space until multiple of 8)
189                 _SysDebug("TODO: VT100 Support \\t tab");
190                 Display_AddText(Term, 1, "\t"); // pass the buck for now
191                 return 1;
192         case '\n':
193                 // TODO: Support disabling CR after NL
194                 Display_Newline(Term, 1);
195                 return 1;
196         case '\r':
197                 if( Len >= 2 && Buf[1] == '\n' ) {
198                         // Fast case for \r\n
199                         Display_Newline(Term, 1);
200                         return 2;
201                 }
202                 Display_MoveCursor(Term, 0, INT_MIN);
203                 return 1;
204         }
205
206          int    ret = 0;
207         while( ret < Len )
208         {
209                 switch(*Buf)
210                 {
211                 case '\x1b':
212                 case '\b':
213                 case '\t':
214                 case '\n':
215                 case '\r':
216                         // Force an exit right now
217                         Len = ret;
218                         break;
219                 default:
220                         ret ++;
221                         Buf ++;
222                         break;
223                 }
224         }
225         return -ret;
226 }
227
228 int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf)
229 {
230          int    tmp;
231         switch(Buf[1])
232         {
233         case '[':       // Multibyte, funtime starts    
234                 tmp = Term_HandleVT100_Long(Term, Len-2, Buf+2);
235                 assert(tmp >= 0);
236                 if( tmp == 0 )
237                         return 0;
238                 return tmp + 2;
239         case ']':
240                 tmp = Term_HandleVT100_OSC(Term, Len-2, Buf+2);
241                 assert(tmp >= 0);
242                 if( tmp == 0 )
243                         return 0;
244                 return tmp + 2;
245         
246         case '#':
247                 if( Len == 2 )
248                         return 0;
249                 switch(Buf[2])
250                 {
251                 case 8:
252                         _SysDebug("TODO \\e#%c DECALN - Fill screen with 'E'", Buf[2]);
253                         break;
254                 default:
255                         _SysDebug("Unknown \\e#%c", Buf[2]);
256                         break;
257                 }
258                 return 3;
259                 
260         case '=':
261                 _SysDebug("TODO: \\e= Application Keypad");
262                 return 2;
263         case '>':
264                 _SysDebug("TODO: \\e= Normal Keypad");
265                 return 2;
266         
267         case '(':       // Update G0 charset
268                 tmp = 0; if(0)
269         case ')':       // Update G1 charset
270                 tmp = 1; if(0)
271         case '*':
272                 tmp = 2; if(0)
273         case '+':
274                 tmp = 3;
275                 
276                 if( Len <= 2 )
277                         return 0;       // We need more
278                 switch(Buf[2])
279                 {
280                 case '0':       // DEC Special Character/Linedrawing set
281                 case 'A':       // UK
282                 case 'B':       // US ASCII
283                         break;
284                 }
285                 return 3;
286         case '7':
287                 // Save cursor
288                 Display_SaveCursor(Term);
289                 return 2;
290         case '8':
291                 // Restore cursor
292                 Display_RestoreCursor(Term);
293                 return 2;
294         case 'D':
295                 // Cursor down, if at bottom scroll
296                 Display_MoveCursor(Term, 1, 0);
297                 // TODO: Scroll if at bottom (impl in _MoveCursor)
298                 return 2;
299         case 'E':
300                 Display_MoveCursor(Term, 1, INT_MIN);
301                 // TODO: Scroll if at bottom (impl in _MoveCursor)
302                 return 2;
303         case 'M':
304                 // Cursor up, scroll if at top
305                 Display_MoveCursor(Term, -1, 0);
306                 return 2;
307         default:
308                 _SysDebug("Unknown VT100 \\e%c", Buf[1]);
309                 return 2;
310         }
311 }
312
313 int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
314 {
315         tVT100State     *st = Display_GetTermState(Term);
316         char    c;
317          int    argc = 0, j = 0;
318          int    args[6] = {0,0,0,0,0,0};
319          int    bQuestionMark = 0;
320         
321         // Get Arguments
322         if(j == Len)    return 0;
323         c = Buffer[j++];
324         if(c == '?')
325         {
326                 bQuestionMark = 1;
327                 if(j == Len)    return 0;
328                 c = Buffer[j++];
329         }
330         if( ('0' <= c && c <= '9') || c == ';' )
331         {
332                 if(c == ';')
333                         argc ++;
334                 do {
335                         if(c == ';') {
336                                 if(j == Len)    return 0;
337                                 c = Buffer[j++];
338                         }
339                         while('0' <= c && c <= '9') {
340                                 args[argc] *= 10;
341                                 args[argc] += c-'0';
342                                 if(j == Len)    return 0;
343                                 c = Buffer[j++];
344                         }
345                         argc ++;
346                 } while(c == ';');
347         }
348         
349         // Get Command
350         if( !isalpha(c) ) {
351                 // Bother.
352                 _SysDebug("Unexpected char 0x%x in VT100 escape code '\\e[%.*s'", c,
353                         Len, Buffer);
354                 return j;
355         }
356
357         if( bQuestionMark )
358         {
359                  int    set = 0;
360                 // Special commands
361                 switch( c )
362                 {
363                 case 'h':       // set
364                         set = 1;
365                 case 'l':       // unset
366                         switch(args[0])
367                         {
368                         case  1:        // Aplication cursor keys
369                                 _SysDebug("TODO: \\e[?1%c Application cursor keys", c);
370                                 break;
371                         case  3:        // 132 Column mode
372                                 _SysDebug("TODO: \\e[?3%c 132 Column mode", c);
373                                 break;
374                         case  4:        // Smooth (Slow) Scroll
375                                 _SysDebug("TODO: \\e[?4%c Smooth (Slow) Scroll", c);
376                                 break;
377                         case  5:        // Reverse Video
378                                 _SysDebug("TODO: \\e[?5%c Reverse Video", c);
379                                 break;
380                         case  6:        // Origin Mode
381                                 _SysDebug("TODO: \\e[?6%c Origin Mode", c);
382                                 break;
383                         case 12:
384                                 //_SysDebug("TODO: \\e[?25%c Start/Stop blinking cursor", c);
385                                 //Display_SolidCursor(Term, !set);
386                                 break;
387                         case 25:        // Hide cursor
388                                 //_SysDebug("TODO: \\e[?25%c Show/Hide cursor", c);
389                                 //Display_ShowCursor(Term, set);
390                                 break;
391                         case 1047:      // Alternate buffer
392                                 Display_ShowAltBuffer(Term, set);
393                                 break;
394                         case 1048:      // Save/restore cursor in DECSC
395                                 _SysDebug("TODO: \\e[?1048%c Save/Restore cursor", c);
396                                 break;
397                         case 1049:      // Save/restore cursor in DECSC and use alternate buffer
398                                 _SysDebug("TODO: \\e[?1049%c Save/Restore cursor", c);
399                                 Display_ShowAltBuffer(Term, set);
400                                 break;
401                         default:
402                                 _SysDebug("TODO: \\e[?%i%c Unknow DEC private mode", args[0], c);
403                                 break;
404                         }
405                         break;
406                 default:
407                         _SysDebug("Unknown VT100 extended escape char 0x%x", c);
408                         break;
409                 }
410         }
411         else
412         {
413                 // Standard commands
414                 switch( c )
415                 {
416                 case 'A':
417                         Display_MoveCursor(Term, -(args[0] != 0 ? args[0] : 1), 0);
418                         break;
419                 case 'B':
420                         Display_MoveCursor(Term, (args[0] != 0 ? args[0] : 1), 0);
421                         break;
422                 case 'C':
423                         Display_MoveCursor(Term, 0, (args[0] != 0 ? args[0] : 1));
424                         break;
425                 case 'D':
426                         Display_MoveCursor(Term, 0, -(args[0] != 0 ? args[0] : 1));
427                         break;
428                 case 'H':
429                         if( argc != 2 ) {
430                         }
431                         else {
432                                 // Adjust 1-based cursor position to 0-based
433                                 Display_SetCursor(Term, args[0]-1, args[1]-1);
434                         }
435                         break;
436                 case 'J':       // Clear lines
437                         switch( args[0] )
438                         {
439                         case 0:
440                                 Display_ClearLines(Term, 1);    // Down
441                                 break;
442                         case 1:
443                                 Display_ClearLines(Term, -1);   // Up
444                                 break;
445                         case 2:
446                                 Display_ClearLines(Term, 0);    // All
447                                 break;
448                         default:
449                                 _SysDebug("Unknown VT100 %i J", args[0]);
450                                 break;
451                         }
452                         break;
453                 case 'K':
454                         switch( args[0] )
455                         {
456                         case 0: // To EOL
457                                 Display_ClearLine(Term, 1);
458                                 break;
459                         case 1: // To SOL
460                                 Display_ClearLine(Term, -1);
461                                 break;
462                         case 2:
463                                 Display_ClearLine(Term, 0);
464                                 break;
465                         default:
466                                 _SysDebug("Unknown VT100 %i K", args[0]);
467                                 break;
468                         }
469                 case 'S':       // Scroll up n=1
470                         Display_ScrollDown(Term, -(argc >= 1 ? args[0] : 1));
471                         break;
472                 case 'T':       // Scroll down n=1
473                         Display_ScrollDown(Term, (argc >= 1 ? args[0] : 1));
474                         break;
475                 case 'c':       // Send Device Attributes
476                         switch(args[0])
477                         {
478                         case 0: // Request attributes from terminal
479                                 // "VT100 with Advanced Video Option" (same as screen returns)
480                                 Display_SendInput(Term, "\x1b[?1;2c");
481                                 break;
482                         default:
483                                 _SysDebug("TODO: Request device attributes \\e[%ic", args[0]);
484                                 break;
485                         }
486                         break;
487                 case 'f':
488                         if( argc != 2 ) {
489                                 Display_SetCursor(Term, 0, 0);
490                         }
491                         else {
492                                 // Adjust 1-based cursor position to 0-based
493                                 Display_SetCursor(Term, args[0]-1, args[1]-1);
494                         }
495                         break;
496                 case 'h':
497                 case 'l':
498                         for( int i = 0; i < argc; i ++ )
499                         {
500                                 switch(args[i])
501                                 {
502                                 default:
503                                         _SysDebug("Unknown VT100 mode \e[%i%c",
504                                                 args[i], c);
505                                         break;
506                                 }
507                         }
508                         break;
509                 case 'm':
510                         if( argc == 0 )
511                         {
512                                 // Reset
513                                 Display_ResetAttributes(Term);
514                         }
515                         else
516                         {
517                                 for( int i = 0; i < argc; i ++ )
518                                 {
519                                         switch(args[i])
520                                         {
521                                         case 0:
522                                                 st->Flags = 0;
523                                                 Display_ResetAttributes(Term);
524                                                 break;
525                                         case 1:
526                                                 st->Flags |= FLAG_BOLD;
527                                                 Display_SetForeground( Term, caVT100Colours[st->CurFG + 8] );
528                                                 break;
529                                         case 2:
530                                                 _SysDebug("TODO: \\e[2m - Reverse");
531                                                 break;
532                                         case 4:
533                                                 _SysDebug("TODO: \\e[4m - Underscore");
534                                                 break;
535                                         case 7:
536                                                 _SysDebug("TODO: \\e[7m - Reverse");
537                                                 break;
538                                         case 24:        // Not underlined
539                                         case 27:        // Not inverse
540                                                 break;
541                                         case 30 ... 37:
542                                                 st->CurFG = args[i]-30;
543                                                 if(0)
544                                         case 39:
545                                                 st->CurFG = 7;
546                                                 Display_SetForeground( Term,
547                                                         caVT100Colours[ st->CurFG + (st->Flags&FLAG_BOLD?8:0) ] );
548                                                 break;
549                                         case 40 ... 47:
550                                                 st->CurBG = args[i]-40;
551                                                 if(0)
552                                         case 49:
553                                                 st->CurBG = 0;
554                                                 Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
555                                                 break;
556                                         default:
557                                                 _SysDebug("Unknown mode set \\e[%im", args[i]);
558                                                 break;
559                                         } 
560                                 }
561                         }
562                         break;
563                 // Device Status Report
564                 case 'n':
565                         break;
566                 // Set scrolling region
567                 case 'r':
568                         Display_SetScrollArea(Term, args[0], (args[1] - args[0]));
569                         break;
570                 
571                 case 's':
572                         Display_SaveCursor(Term);
573                         break;
574                 case 'u':
575                         Display_RestoreCursor(Term);
576                         break;
577                 default:
578                         _SysDebug("Unknown VT100 long escape char 0x%x '%c'", c, c);
579                         break;
580                 }
581         }
582         return j;
583 }
584
585 int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf)
586 {
587         tVT100State     *st = Display_GetTermState(Term);
588
589          int    ofs = 0;
590         // OSC Ps ; Pt [ST/BEL]
591         if(Len < 2)     return 0;       // Need moar
592
593          int    Ps = 0;
594         while( ofs < Len && isdigit(Buf[ofs]) ) {
595                 Ps = Ps * 10 + (Buf[ofs] - '0');
596                 ofs ++;
597         }
598
599         if( ofs == Len )        return 0;
600         if( Buf[ofs] != ';' ) {
601                 // Error
602                 st->Mode = MODE_STRING;
603                 st->StringType = STRING_IGNORE;
604                 return ofs;
605         }
606         ofs ++;
607
608         switch(Ps)
609         {
610         case 0: // Icon Name + Window Title
611         case 1: // Icon Name
612         case 2: // Window Title
613                 st->Mode = MODE_STRING;
614                 st->StringType = STRING_TITLE;
615                 break;
616         case 3: // Set X Property
617                 _SysDebug("TODO: \\e]3; Support X properties");
618                 st->Mode = MODE_STRING;
619                 st->StringType = STRING_IGNORE;
620                 break;
621         case 4: // Change colour number
622         case 5: // Change special colour number
623                 _SysDebug("TODO: \\e]%i;c; Support X properties", Ps);
624                 st->Mode = MODE_STRING;
625                 st->StringType = STRING_IGNORE;
626                 break;  
627         case 10:        // Change foreground to Pt
628         case 11:        // Change background to Pt
629         case 52:
630                 // TODO: Can be Pt = [cps01234567]*;<str>
631                 // > Clipboard data in base-64, cut/primary/select buffers 0-7
632                 // > <str>='?' returns the clipbard in the same format
633         default:
634                 _SysDebug("Unknown VT100 OSC \\e]%i;", Ps);
635                 st->Mode = MODE_STRING;
636                 st->StringType = STRING_IGNORE;
637                 break;
638         }
639         return ofs;
640 }
641

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