3 * - By John Hodge (thePowersGang)
6 * - VT100/xterm Emulation
9 #include "include/vt100.h"
10 #include "include/display.h"
11 #include <ctype.h> // isalpha
13 # define _SysDebug(v...) Debug("VT100 "v)
15 # include <acess/sys.h> // _SysDebug
18 # include <stdlib.h> // malloc/free
31 #define FLAG_BOLD 0x01
32 #define FLAG_REVERSE 0x02
38 enum eExcapeMode Mode;
40 enum eStringType StringType;
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,
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);
57 static inline int min(int a, int b)
62 int _locate_eos(size_t Len, const char *Buf)
64 for( size_t ret = 0; ret < Len; ret ++ )
66 if( Buf[ret] == '\007' )
68 if( Buf[ret] == '\x9c' )
70 if( ret+1 < Len && Buf[ret] == '\x1b' && Buf[ret+1] == '\\' )
76 int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
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);
84 st = malloc( sizeof(*st) );
85 memset(st, 0, sizeof(*st));
86 Display_SetTermState(Term, st);
89 if( st->Mode == MODE_IGNORE ) {
90 st->Mode = MODE_NORMAL;
91 // Used for multi-byte EOS
92 _SysDebug("Ignore 1 '%c'", *Buf);
95 else if( st->Mode == MODE_STRING )
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;
107 _SysDebug("pos=%i", pos);
108 _SysDebug("Buf[+%zi] = '%.*s'", bytes, bytes, Buf);
109 // Only apply when we hit EOS at the start
112 switch(st->StringType)
115 Display_SetTitle(Term, st->StringCache);
120 free(st->StringCache);
124 if( *Buf == '\x1b' ) {
125 st->Mode = MODE_IGNORE;
129 st->Mode = MODE_NORMAL;
136 if( inc_len > 0 || *Buf == '\x1b' )
138 // Handle VT100 (like) escape sequence
139 int new_bytes = min(MAX_VT100_ESCAPE_LEN - inc_len, Len);
141 int old_inc_len = inc_len;
142 memcpy(inc_buf + inc_len, Buf, new_bytes);
144 if( new_bytes == 0 ) {
145 _SysDebug("Term_HandleVT100: Hit max? (Len=%i) Flushing cache", Len);
150 inc_len += new_bytes;
151 //_SysDebug("inc_buf = %i '%.*s'", inc_len, inc_len, inc_buf);
154 return 1; // Skip 1 character (the '\x1b')
156 ret = Term_HandleVT100_Short(Term, inc_len, inc_buf);
160 // Check that we actually used the new data (as should have happened)
161 if( ret <= old_inc_len ) {
162 _SysDebug("Term_HandleVT100: ret(%i) <= old_inc_len(%i) '%.*s'\n",
163 ret, old_inc_len, inc_len, inc_buf);
164 assert(ret > old_inc_len);
166 //_SysDebug("%i bytes of escape code '%.*s' (return %i)",
167 // ret, ret, inc_buf, ret-old_inc_len);
168 ret -= old_inc_len; // counter cached bytes
177 // TODO: Need to handle \t and ^A-Z
179 Display_MoveCursor(Term, 0, -1);
180 Display_AddText(Term, 1, " ");
181 Display_MoveCursor(Term, 0, -1);
184 // TODO: tab (get current cursor pos, space until multiple of 8)
185 _SysDebug("TODO: VT100 Support \\t tab");
188 // TODO: Support disabling CR after NL
189 Display_Newline(Term, 1);
192 if( Len >= 2 && Buf[1] == '\n' ) {
193 // Fast case for \r\n
194 Display_Newline(Term, 1);
197 Display_MoveCursor(Term, 0, INT_MIN);
211 // Force an exit right now
223 int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf)
228 case '[': // Multibyte, funtime starts
229 tmp = Term_HandleVT100_Long(Term, Len-2, Buf+2);
235 tmp = Term_HandleVT100_OSC(Term, Len-2, Buf+2);
241 _SysDebug("TODO: \\e= Application Keypad");
244 _SysDebug("TODO: \\e= Normal Keypad");
247 case '(': // Update G0 charset
249 case ')': // Update G1 charset
257 return 0; // We need more
260 case '0': // DEC Special Character/Linedrawing set
262 case 'B': // US ASCII
266 // xterm C1 \eD and \eM are 'Index' and 'Reverse Index'
267 // - Aparently scroll?
269 // Display_ScrollDown(Term, 1);
273 // Display_ScrollDown(Term, -1);
277 _SysDebug("Unknown VT100 \\e%c", Buf[1]);
282 int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
284 tVT100State *st = Display_GetTermState(Term);
287 int args[6] = {0,0,0,0,0,0};
288 int bQuestionMark = 0;
291 if(j == Len) return 0;
295 if(j == Len) return 0;
298 if( '0' <= c && c <= '9' )
302 if(j == Len) return 0;
305 while('0' <= c && c <= '9') {
308 if(j == Len) return 0;
318 _SysDebug("Unexpected char 0x%x in VT100 escape code '\\e[%.*s'", c,
334 case 1: // Aplication cursor keys
335 _SysDebug("TODO: \\e[?1%c Application cursor keys", c);
338 _SysDebug("TODO: \\e[?25%c Start/Stop blinking cursor", c);
340 case 25: // Hide cursor
341 _SysDebug("TODO: \\e[?25%c Show/Hide cursor", c);
343 case 1047: // Alternate buffer
344 Display_ShowAltBuffer(Term, set);
346 case 1048: // Save/restore cursor in DECSC
347 _SysDebug("TODO: \\e[?1048%c Save/Restore cursor", c);
349 case 1049: // Save/restore cursor in DECSC and use alternate buffer
350 _SysDebug("TODO: \\e[?1049%c Save/Restore cursor", c);
351 Display_ShowAltBuffer(Term, set);
354 _SysDebug("TODO: \\e[?%i%c Unknow DEC private mode", args[0], c);
359 _SysDebug("Unknown VT100 extended escape char 0x%x", c);
369 Display_MoveCursor(Term, -(argc >= 1 ? args[0] : 1), 0);
372 Display_MoveCursor(Term, (argc >= 1 ? args[0] : 1), 0);
375 Display_MoveCursor(Term, 0, (argc >= 1 ? args[0] : 1));
378 Display_MoveCursor(Term, 0, -(argc >= 1 ? args[0] : 1));
384 // Adjust 1-based cursor position to 0-based
385 Display_SetCursor(Term, args[0]-1, args[1]-1);
388 case 'J': // Clear lines
392 Display_ClearLines(Term, 1); // Down
395 Display_ClearLines(Term, -1); // Up
398 Display_ClearLines(Term, 0); // All
401 _SysDebug("Unknown VT100 %i J", args[0]);
409 Display_ClearLine(Term, 1);
412 Display_ClearLine(Term, -1);
415 Display_ClearLine(Term, 0);
418 _SysDebug("Unknown VT100 %i K", args[0]);
421 case 'S': // Scroll up n=1
422 Display_ScrollDown(Term, -(argc >= 1 ? args[0] : 1));
424 case 'T': // Scroll down n=1
425 Display_ScrollDown(Term, (argc >= 1 ? args[0] : 1));
431 Display_ResetAttributes(Term);
435 for( int i = 0; i < argc; i ++ )
441 Display_ResetAttributes(Term);
444 st->Flags |= FLAG_BOLD;
445 Display_SetForeground( Term, caVT100Colours[st->CurFG + 8] );
448 _SysDebug("TODO: VT100 \\e[1m - Reverse");
451 st->CurFG = args[i]-30;
455 Display_SetForeground( Term,
456 caVT100Colours[ st->CurFG + (st->Flags&FLAG_BOLD?8:0) ] );
459 st->CurBG = args[i]-40;
463 Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
466 _SysDebug("TODO: VT100 \\e[%im", args[i]);
472 // Set scrolling region
474 Display_SetScrollArea(Term, args[0], (args[1] - args[0]));
478 Display_SaveCursor(Term);
481 Display_RestoreCursor(Term);
484 _SysDebug("Unknown VT100 long escape char 0x%x '%c'", c, c);
491 int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf)
493 tVT100State *st = Display_GetTermState(Term);
496 // OSC Ps ; Pt [ST/BEL]
497 if(Len < 2) return 0; // Need moar
500 while( ofs < Len && isdigit(Buf[ofs]) ) {
501 Ps = Ps * 10 + (Buf[ofs] - '0');
505 if( ofs == Len ) return 0;
506 if( Buf[ofs] != ';' ) {
508 st->Mode = MODE_STRING;
509 st->StringType = STRING_IGNORE;
516 case 0: // Icon Name + Window Title
518 case 2: // Window Title
519 st->Mode = MODE_STRING;
520 st->StringType = STRING_TITLE;
522 case 3: // Set X Property
523 _SysDebug("TODO: \\e]3; Support X properties");
524 st->Mode = MODE_STRING;
525 st->StringType = STRING_IGNORE;
527 case 4: // Change colour number
528 case 5: // Change special colour number
529 _SysDebug("TODO: \\e]%i;c; Support X properties", Ps);
530 st->Mode = MODE_STRING;
531 st->StringType = STRING_IGNORE;
533 case 10: // Change foreground to Pt
534 case 11: // Change background to Pt
536 // TODO: Can be Pt = [cps01234567]*;<str>
537 // > Clipboard data in base-64, cut/primary/select buffers 0-7
538 // > <str>='?' returns the clipbard in the same format
540 _SysDebug("Unknown VT100 OSC \\e]%i;", Ps);
541 st->Mode = MODE_STRING;
542 st->StringType = STRING_IGNORE;