91967ff611d91380d74d6e2b0472275e09aaee43
[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 #endif
20
21 enum eExcapeMode {
22         MODE_NORMAL,
23         MODE_IGNORE,
24         MODE_STRING
25 };
26 enum eStringType {
27         STRING_IGNORE,
28         STRING_TITLE
29 };
30
31 #define FLAG_BOLD       0x01
32 #define FLAG_REVERSE    0x02
33
34 typedef struct {
35         uint32_t        Flags;
36         int     CurFG, CurBG;
37         
38         enum eExcapeMode        Mode;
39         
40         enum eStringType        StringType;
41         size_t  StringLen;
42         char    *StringCache;
43 } tVT100State;
44
45 const uint32_t  caVT100Colours[] = {
46         // Black,      Red,    Green,   Yellow,     Blue,  Magenta,     Cyan,      Gray
47         // Same again, but bright
48         0x000000, 0x770000, 0x007700, 0x777700, 0x000077, 0x770077, 0x007777, 0xAAAAAA,
49         0xCCCCCC, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF, 
50 };
51
52  int    _locate_eos(size_t Len, const char *Buf);
53  int    Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf);
54  int    Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf);
55  int    Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf);
56
57 static inline int min(int a, int b)
58 {
59         return a < b ? a : b;
60 }
61
62 int _locate_eos(size_t Len, const char *Buf)
63 {
64         for( size_t ret = 0; ret < Len; ret ++ )
65         {
66                 if( Buf[ret] == '\007' )
67                         return ret;
68                 if( Buf[ret] == '\x9c' )
69                         return ret;
70                 if( ret+1 < Len && Buf[ret] == '\x1b' && Buf[ret+1] == '\\' )
71                         return ret;
72         }
73         return -1;
74 }
75
76 int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
77 {
78         #define MAX_VT100_ESCAPE_LEN    16
79         static char     inc_buf[MAX_VT100_ESCAPE_LEN];
80         static int      inc_len = 0;
81         tVT100State     *st = Display_GetTermState(Term);
82         
83         if( st == NULL ) {
84                 st = malloc( sizeof(*st) );
85                 memset(st, 0, sizeof(*st));
86                 Display_SetTermState(Term, st);
87         }
88
89         if( st->Mode == MODE_IGNORE ) {
90                 st->Mode = MODE_NORMAL;
91                 // Used for multi-byte EOS
92                 _SysDebug("Ignore 1 '%c'", *Buf);
93                 return 1;
94         }
95         else if( st->Mode == MODE_STRING )
96         {
97                 // We're in a string mode
98                 int pos = _locate_eos(Len, Buf);
99                 size_t bytes = (pos >= 0 ? pos : Len);
100                 char *tmp = realloc(st->StringCache, st->StringLen + bytes+1);
101                 if(!tmp)        return bytes;
102                 st->StringCache = tmp;
103                 memcpy(tmp+st->StringLen, Buf, bytes);
104                 tmp[st->StringLen+bytes] = 0;
105                 st->StringLen += bytes;
106                 
107                 _SysDebug("pos=%i", pos);
108                 _SysDebug("Buf[+%zi] = '%.*s'", bytes, bytes, Buf);
109                 // Only apply when we hit EOS at the start
110                 if( pos != 0 )
111                         return bytes;
112                 switch(st->StringType)
113                 {
114                 case STRING_TITLE:
115                         Display_SetTitle(Term, st->StringCache);
116                         break;
117                 case STRING_IGNORE:
118                         break;
119                 }
120                 free(st->StringCache);
121                 st->StringCache = 0;
122                 st->StringLen = 0;
123                 
124                 if( *Buf == '\x1b' ) {
125                         st->Mode = MODE_IGNORE;
126                         // skip the '\\'
127                 }
128                 else
129                         st->Mode = MODE_NORMAL;
130                 return 1;
131         }
132         else {
133                 // fall through
134         }
135
136         if( inc_len > 0 || *Buf == '\x1b' )
137         {
138                 // Handle VT100 (like) escape sequence
139                  int    new_bytes = min(MAX_VT100_ESCAPE_LEN - inc_len, Len);
140                  int    ret = 0;
141                  int    old_inc_len = inc_len;
142                 memcpy(inc_buf + inc_len, Buf, new_bytes);
143
144                 if( new_bytes == 0 ) {
145                         _SysDebug("Term_HandleVT100: Hit max? (Len=%i) Flushing cache", Len);
146                         inc_len = 0;
147                         return 0;
148                 }
149
150                 inc_len += new_bytes;
151                 //_SysDebug("inc_buf = %i '%.*s'", inc_len, inc_len, inc_buf);
152
153                 if( inc_len <= 1 )
154                         return 1;       // Skip 1 character (the '\x1b')
155
156                 ret = Term_HandleVT100_Short(Term, inc_len, inc_buf);
157
158                 if( ret != 0 ) {
159                         inc_len = 0;
160                         assert(ret > old_inc_len);
161                         //_SysDebug("%i bytes of escape code '%.*s' (return %i)",
162                         //      ret, ret, inc_buf, ret-old_inc_len);
163                         ret -= old_inc_len;     // counter cached bytes
164                 }
165                 else
166                         ret = new_bytes;
167                 return ret;
168         }
169
170         switch( *Buf )
171         {
172         // TODO: Need to handle \t and ^A-Z
173         case '\b':
174                 Display_MoveCursor(Term, 0, -1);
175                 Display_AddText(Term, 1, " ");
176                 Display_MoveCursor(Term, 0, -1);
177                 return 1;
178         case '\t':
179                 // TODO: tab (get current cursor pos, space until multiple of 8)
180                 _SysDebug("TODO: VT100 Support \\t tab");
181                 return 1;
182         case '\n':
183                 // TODO: Support disabling CR after NL
184                 Display_Newline(Term, 1);
185                 return 1;
186         case '\r':
187                 if( Len >= 2 && Buf[1] == '\n' ) {
188                         // Fast case for \r\n
189                         Display_Newline(Term, 1);
190                         return 2;
191                 }
192                 Display_MoveCursor(Term, 0, INT_MIN);
193                 return 1;
194         }
195
196          int    ret = 0;
197         while( ret < Len )
198         {
199                 switch(*Buf)
200                 {
201                 case '\x1b':
202                 case '\b':
203                 case '\t':
204                 case '\n':
205                 case '\r':
206                         // Force an exit right now
207                         Len = ret;
208                         break;
209                 default:
210                         ret ++;
211                         Buf ++;
212                         break;
213                 }
214         }
215         return -ret;
216 }
217
218 int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf)
219 {
220          int    tmp;
221         switch(Buf[1])
222         {
223         case '[':       // Multibyte, funtime starts    
224                 tmp = Term_HandleVT100_Long(Term, Len-2, Buf+2);
225                 assert(tmp >= 0);
226                 if( tmp == 0 )
227                         return 0;
228                 return tmp + 2;
229         case ']':
230                 tmp = Term_HandleVT100_OSC(Term, Len-2, Buf+2);
231                 assert(tmp >= 0);
232                 if( tmp == 0 )
233                         return 0;
234                 return tmp + 2;
235         case '=':
236                 _SysDebug("TODO: \\e= Application Keypad");
237                 return 2;
238         case '>':
239                 _SysDebug("TODO: \\e= Normal Keypad");
240                 return 2;
241         
242         case '(':       // Update G0 charset
243                 tmp = 0; if(0)
244         case ')':       // Update G1 charset
245                 tmp = 1; if(0)
246         case '*':
247                 tmp = 2; if(0)
248         case '+':
249                 tmp = 3;
250                 
251                 if( Len <= 2 )
252                         return 0;       // We need more
253                 switch(Buf[2])
254                 {
255                 case '0':       // DEC Special Character/Linedrawing set
256                 case 'A':       // UK
257                 case 'B':       // US ASCII
258                         break;
259                 }
260                 return 3;
261         // xterm C1 \eD and \eM are 'Index' and 'Reverse Index'
262         // - Aparently scroll?
263         //case 'D':
264         //      Display_ScrollDown(Term, 1);
265         //      ret = 2;
266         //      break;
267         //case 'M':
268         //      Display_ScrollDown(Term, -1);
269         //      ret = 2;
270         //      break;
271         default:
272                 _SysDebug("Unknown VT100 \\e%c", Buf[1]);
273                 return 2;
274         }
275 }
276
277 int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
278 {
279         tVT100State     *st = Display_GetTermState(Term);
280         char    c;
281          int    argc = 0, j = 0;
282          int    args[6] = {0,0,0,0,0,0};
283          int    bQuestionMark = 0;
284         
285         // Get Arguments
286         if(j == Len)    return 0;
287         c = Buffer[j++];
288         if(c == '?') {
289                 bQuestionMark = 1;
290                 if(j == Len)    return 0;
291                 c = Buffer[j++];
292         }
293         if( '0' <= c && c <= '9' )
294         {
295                 do {
296                         if(c == ';') {
297                                 if(j == Len)    return 0;
298                                 c = Buffer[j++];
299                         }
300                         while('0' <= c && c <= '9') {
301                                 args[argc] *= 10;
302                                 args[argc] += c-'0';
303                                 if(j == Len)    return 0;
304                                 c = Buffer[j++];
305                         }
306                         argc ++;
307                 } while(c == ';');
308         }
309         
310         // Get Command
311         if( !isalpha(c) ) {
312                 // Bother.
313                 _SysDebug("Unexpected char 0x%x in VT100 escape code '\\e[%.*s'", c,
314                         Len, Buffer);
315                 return j;
316         }
317
318         if( bQuestionMark )
319         {
320                  int    set = 0;
321                 // Special commands
322                 switch( c )
323                 {
324                 case 'h':       // set
325                         set = 1;
326                 case 'l':       // unset
327                         switch(args[0])
328                         {
329                         case  1:        // Aplication cursor keys
330                                 _SysDebug("TODO: \\e[?1%c Application cursor keys", c);
331                                 break;
332                         case 25:        // Hide cursor
333                                 _SysDebug("TODO: \\e[?25%c Show/Hide cursor", c);
334                                 break;
335                         case 1047:      // Alternate buffer
336                                 Display_ShowAltBuffer(Term, set);
337                                 break;
338                         case 1048:      // Save/restore cursor in DECSC
339                                 _SysDebug("TODO: \\e[?1048%c Save/Restore cursor", c);
340                                 break;
341                         case 1049:      // Save/restore cursor in DECSC and use alternate buffer
342                                 _SysDebug("TODO: \\e[?1049%c Save/Restore cursor", c);
343                                 Display_ShowAltBuffer(Term, set);
344                                 break;
345                         default:
346                                 _SysDebug("TODO: \\e[?%i%c Unknow DEC private mode", args[0], c);
347                                 break;
348                         }
349                         break;
350                 default:
351                         _SysDebug("Unknown VT100 extended escape char 0x%x", c);
352                         break;
353                 }
354         }
355         else
356         {
357                 // Standard commands
358                 switch( c )
359                 {
360                 case 'A':
361                         Display_MoveCursor(Term, -(argc >= 1 ? args[0] : 1), 0);
362                         break;
363                 case 'B':
364                         Display_MoveCursor(Term, (argc >= 1 ? args[0] : 1), 0);
365                         break;
366                 case 'C':
367                         Display_MoveCursor(Term, 0, (argc >= 1 ? args[0] : 1));
368                         break;
369                 case 'D':
370                         Display_MoveCursor(Term, 0, -(argc >= 1 ? args[0] : 1));
371                         break;
372                 case 'H':
373                         if( argc != 2 ) {
374                         }
375                         else {
376                                 Display_SetCursor(Term, args[0], args[1]);
377                         }
378                         break;
379                 case 'J':       // Clear lines
380                         switch( args[0] )
381                         {
382                         case 0:
383                                 Display_ClearLines(Term, 1);    // Down
384                                 break;
385                         case 1:
386                                 Display_ClearLines(Term, -1);   // Up
387                                 break;
388                         case 2:
389                                 Display_ClearLines(Term, 0);    // All
390                                 break;
391                         default:
392                                 _SysDebug("Unknown VT100 %i J", args[0]);
393                                 break;
394                         }
395                         break;
396                 case 'K':
397                         switch( args[0] )
398                         {
399                         case 0: // To EOL
400                                 Display_ClearLine(Term, 1);
401                                 break;
402                         case 1: // To SOL
403                                 Display_ClearLine(Term, -1);
404                                 break;
405                         case 2:
406                                 Display_ClearLine(Term, 0);
407                                 break;
408                         default:
409                                 _SysDebug("Unknown VT100 %i K", args[0]);
410                                 break;
411                         }
412                 case 'S':       // Scroll up n=1
413                         Display_ScrollDown(Term, -(argc >= 1 ? args[0] : 1));
414                         break;
415                 case 'T':       // Scroll down n=1
416                         Display_ScrollDown(Term, (argc >= 1 ? args[0] : 1));
417                         break;
418                 case 'm':
419                         if( argc == 0 )
420                         {
421                                 // Reset
422                                 Display_ResetAttributes(Term);
423                         }
424                         else
425                         {
426                                 for( int i = 0; i < argc; i ++ )
427                                 {
428                                         switch(args[i])
429                                         {
430                                         case 0:
431                                                 st->Flags = 0;
432                                                 Display_ResetAttributes(Term);
433                                                 break;
434                                         case 1:
435                                                 st->Flags |= FLAG_BOLD;
436                                                 Display_SetForeground( Term, caVT100Colours[st->CurFG + 8] );
437                                                 break;
438                                         case 2:
439                                                 _SysDebug("TODO: VT100 \\e[1m - Reverse");
440                                                 break;
441                                         case 30 ... 37:
442                                                 st->CurFG = args[i]-30;
443                                                 if(0)
444                                         case 39:
445                                                 st->CurFG = 7;
446                                                 Display_SetForeground( Term,
447                                                         caVT100Colours[ st->CurFG + (st->Flags&FLAG_BOLD?8:0) ] );
448                                                 break;
449                                         case 40 ... 47:
450                                                 st->CurBG = args[i]-40;
451                                                 if(0)
452                                         case 49:
453                                                 st->CurBG = 0;
454                                                 Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
455                                                 break;
456                                         default:
457                                                 _SysDebug("TODO: VT100 \\e[%im", args[i]);
458                                                 break;
459                                         } 
460                                 }
461                         }
462                         break;
463                 // Set scrolling region
464                 case 'r':
465                         Display_SetScrollArea(Term, args[0], (args[1] - args[0]));
466                         break;
467                 
468                 case 's':
469                         Display_SaveCursor(Term);
470                         break;
471                 case 'u':
472                         Display_RestoreCursor(Term);
473                         break;
474                 default:
475                         _SysDebug("Unknown VT100 long escape char 0x%x '%c'", c, c);
476                         break;
477                 }
478         }
479         return j;
480 }
481
482 int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf)
483 {
484         tVT100State     *st = Display_GetTermState(Term);
485
486          int    ofs = 0;
487         // OSC Ps ; Pt [ST/BEL]
488         if(Len < 2)     return 0;       // Need moar
489
490          int    Ps = 0;
491         while( ofs < Len && isdigit(Buf[ofs]) ) {
492                 Ps = Ps * 10 + (Buf[ofs] - '0');
493                 ofs ++;
494         }
495
496         if( ofs == Len )        return 0;
497         if( Buf[ofs] != ';' ) {
498                 // Error
499                 st->Mode = MODE_STRING;
500                 st->StringType = STRING_IGNORE;
501                 return ofs;
502         }
503         ofs ++;
504
505         switch(Ps)
506         {
507         case 0: // Icon Name + Window Title
508         case 1: // Icon Name
509         case 2: // Window Title
510                 st->Mode = MODE_STRING;
511                 st->StringType = STRING_TITLE;
512                 break;
513         case 3: // Set X Property
514                 _SysDebug("TODO: \\e]3; Support X properties");
515                 st->Mode = MODE_STRING;
516                 st->StringType = STRING_IGNORE;
517                 break;
518         case 4: // Change colour number
519         case 5: // Change special colour number
520                 _SysDebug("TODO: \\e]%i;c; Support X properties", Ps);
521                 st->Mode = MODE_STRING;
522                 st->StringType = STRING_IGNORE;
523                 break;  
524         case 10:        // Change foreground to Pt
525         case 11:        // Change background to Pt
526         case 52:
527                 // TODO: Can be Pt = [cps01234567]*;<str>
528                 // > Clipboard data in base-64, cut/primary/select buffers 0-7
529                 // > <str>='?' returns the clipbard in the same format
530         default:
531                 _SysDebug("Unknown VT100 OSC \\e]%i;", Ps);
532                 st->Mode = MODE_STRING;
533                 st->StringType = STRING_IGNORE;
534                 break;
535         }
536         return ofs;
537 }
538

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