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

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