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

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