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
20 static inline int MIN(int a, int b)
36 #define FLAG_BOLD 0x01
37 #define FLAG_REVERSE 0x02
39 #define MAX_VT100_ESCAPE_LEN 32
44 char cache[MAX_VT100_ESCAPE_LEN];
47 enum eExcapeMode Mode;
49 enum eStringType StringType;
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,
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);
66 int _locate_eos(size_t Len, const char *Buf)
68 for( size_t ret = 0; ret < Len; ret ++ )
70 if( Buf[ret] == '\007' )
72 if( Buf[ret] == '\x9c' )
74 if( ret+1 < Len && Buf[ret] == '\x1b' && Buf[ret+1] == '\\' )
80 int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf)
82 tVT100State *st = Display_GetTermState(Term);
85 st = malloc( sizeof(*st) );
86 memset(st, 0, sizeof(*st));
87 Display_SetTermState(Term, st);
90 if( st->Mode == MODE_IGNORE ) {
91 st->Mode = MODE_NORMAL;
92 // Used for multi-byte EOS
93 _SysDebug("Ignore 1 '%c'", *Buf);
96 else if( st->Mode == MODE_STRING )
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;
108 _SysDebug("pos=%i", pos);
109 _SysDebug("Buf[+%zi] = '%.*s'", bytes, bytes, Buf);
110 // Only apply when we hit EOS at the start
113 switch(st->StringType)
116 Display_SetTitle(Term, st->StringCache);
121 free(st->StringCache);
125 if( *Buf == '\x1b' ) {
126 st->Mode = MODE_IGNORE;
130 st->Mode = MODE_NORMAL;
137 if( st->cache_len > 0 || *Buf == '\x1b' )
139 // Handle VT100 (like) escape sequence
140 int new_bytes = MIN(MAX_VT100_ESCAPE_LEN - st->cache_len, Len);
142 int old_inc_len = st->cache_len;
144 memcpy(st->cache + st->cache_len, Buf, new_bytes);
146 if( new_bytes == 0 ) {
147 _SysDebug("Term_HandleVT100: Hit max? (Len=%i) Flushing cache", Len);
152 st->cache_len += new_bytes;
153 assert(st->cache_len > old_inc_len);
155 if( st->cache_len <= 1 )
156 return 1; // Skip 1 character (the '\x1b')
158 ret = Term_HandleVT100_Short(Term, st->cache_len, st->cache);
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);
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
174 _SysDebug("Term_HandleVT100: Caching %i bytes '%*C'", ret, ret, st->cache);
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);
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
193 // TODO: Support disabling CR after NL
194 Display_Newline(Term, 1);
197 if( Len >= 2 && Buf[1] == '\n' ) {
198 // Fast case for \r\n
199 Display_Newline(Term, 1);
202 Display_MoveCursor(Term, 0, INT_MIN);
216 // Force an exit right now
228 int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf)
233 case '[': // Multibyte, funtime starts
234 tmp = Term_HandleVT100_Long(Term, Len-2, Buf+2);
240 tmp = Term_HandleVT100_OSC(Term, Len-2, Buf+2);
252 _SysDebug("TODO \\e#%c DECALN - Fill screen with 'E'", Buf[2]);
255 _SysDebug("Unknown \\e#%c", Buf[2]);
261 _SysDebug("TODO: \\e= Application Keypad");
264 _SysDebug("TODO: \\e= Normal Keypad");
267 case '(': // Update G0 charset
269 case ')': // Update G1 charset
277 return 0; // We need more
280 case '0': // DEC Special Character/Linedrawing set
282 case 'B': // US ASCII
288 Display_SaveCursor(Term);
292 Display_RestoreCursor(Term);
295 // Cursor down, if at bottom scroll
296 Display_MoveCursor(Term, 1, 0);
297 // TODO: Scroll if at bottom (impl in _MoveCursor)
300 Display_MoveCursor(Term, 1, INT_MIN);
301 // TODO: Scroll if at bottom (impl in _MoveCursor)
304 // Cursor up, scroll if at top
305 Display_MoveCursor(Term, -1, 0);
308 _SysDebug("Unknown VT100 \\e%c", Buf[1]);
313 int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer)
315 tVT100State *st = Display_GetTermState(Term);
318 int args[6] = {0,0,0,0,0,0};
319 int bQuestionMark = 0;
322 if(j == Len) return 0;
327 if(j == Len) return 0;
330 if( ('0' <= c && c <= '9') || c == ';' )
336 if(j == Len) return 0;
339 while('0' <= c && c <= '9') {
342 if(j == Len) return 0;
352 _SysDebug("Unexpected char 0x%x in VT100 escape code '\\e[%.*s'", c,
368 case 1: // Aplication cursor keys
369 _SysDebug("TODO: \\e[?1%c Application cursor keys", c);
371 case 3: // 132 Column mode
372 _SysDebug("TODO: \\e[?3%c 132 Column mode", c);
374 case 4: // Smooth (Slow) Scroll
375 _SysDebug("TODO: \\e[?4%c Smooth (Slow) Scroll", c);
377 case 5: // Reverse Video
378 _SysDebug("TODO: \\e[?5%c Reverse Video", c);
380 case 6: // Origin Mode
381 _SysDebug("TODO: \\e[?6%c Origin Mode", c);
384 //_SysDebug("TODO: \\e[?25%c Start/Stop blinking cursor", c);
385 //Display_SolidCursor(Term, !set);
387 case 25: // Hide cursor
388 //_SysDebug("TODO: \\e[?25%c Show/Hide cursor", c);
389 //Display_ShowCursor(Term, set);
391 case 1047: // Alternate buffer
392 Display_ShowAltBuffer(Term, set);
394 case 1048: // Save/restore cursor in DECSC
395 _SysDebug("TODO: \\e[?1048%c Save/Restore cursor", c);
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);
402 _SysDebug("TODO: \\e[?%i%c Unknow DEC private mode", args[0], c);
407 _SysDebug("Unknown VT100 extended escape char 0x%x", c);
417 Display_MoveCursor(Term, -(args[0] != 0 ? args[0] : 1), 0);
420 Display_MoveCursor(Term, (args[0] != 0 ? args[0] : 1), 0);
423 Display_MoveCursor(Term, 0, (args[0] != 0 ? args[0] : 1));
426 Display_MoveCursor(Term, 0, -(args[0] != 0 ? args[0] : 1));
432 // Adjust 1-based cursor position to 0-based
433 Display_SetCursor(Term, args[0]-1, args[1]-1);
436 case 'J': // Clear lines
440 Display_ClearLines(Term, 1); // Down
443 Display_ClearLines(Term, -1); // Up
446 Display_ClearLines(Term, 0); // All
449 _SysDebug("Unknown VT100 %i J", args[0]);
457 Display_ClearLine(Term, 1);
460 Display_ClearLine(Term, -1);
463 Display_ClearLine(Term, 0);
466 _SysDebug("Unknown VT100 %i K", args[0]);
470 case 'S': // Scroll up n=1
471 Display_ScrollDown(Term, -(argc >= 1 ? args[0] : 1));
473 case 'T': // Scroll down n=1
474 Display_ScrollDown(Term, (argc >= 1 ? args[0] : 1));
476 case 'c': // Send Device Attributes
479 case 0: // Request attributes from terminal
480 // "VT100 with Advanced Video Option" (same as screen returns)
481 Display_SendInput(Term, "\x1b[?1;2c");
484 _SysDebug("TODO: Request device attributes \\e[%ic", args[0]);
490 Display_SetCursor(Term, 0, 0);
493 // Adjust 1-based cursor position to 0-based
494 Display_SetCursor(Term, args[0]-1, args[1]-1);
499 for( int i = 0; i < argc; i ++ )
504 _SysDebug("Unknown VT100 mode \e[%i%c",
514 Display_ResetAttributes(Term);
518 for( int i = 0; i < argc; i ++ )
524 Display_ResetAttributes(Term);
527 st->Flags |= FLAG_BOLD;
528 Display_SetForeground( Term, caVT100Colours[st->CurFG + 8] );
531 _SysDebug("TODO: \\e[2m - Reverse");
534 _SysDebug("TODO: \\e[4m - Underscore");
537 _SysDebug("TODO: \\e[7m - Reverse");
539 case 24: // Not underlined
540 case 27: // Not inverse
543 st->CurFG = args[i]-30;
547 Display_SetForeground( Term,
548 caVT100Colours[ st->CurFG + (st->Flags&FLAG_BOLD?8:0) ] );
551 st->CurBG = args[i]-40;
555 Display_SetBackground( Term, caVT100Colours[ st->CurBG ] );
558 _SysDebug("Unknown mode set \\e[%im", args[i]);
564 // Device Status Report
567 // Set scrolling region
569 Display_SetScrollArea(Term, args[0], (args[1] - args[0]));
573 Display_SaveCursor(Term);
576 Display_RestoreCursor(Term);
579 _SysDebug("Unknown VT100 long escape char 0x%x '%c'", c, c);
586 int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf)
588 tVT100State *st = Display_GetTermState(Term);
591 // OSC Ps ; Pt [ST/BEL]
592 if(Len < 2) return 0; // Need moar
595 while( ofs < Len && isdigit(Buf[ofs]) ) {
596 Ps = Ps * 10 + (Buf[ofs] - '0');
600 if( ofs == Len ) return 0;
601 if( Buf[ofs] != ';' ) {
603 st->Mode = MODE_STRING;
604 st->StringType = STRING_IGNORE;
611 case 0: // Icon Name + Window Title
613 case 2: // Window Title
614 st->Mode = MODE_STRING;
615 st->StringType = STRING_TITLE;
617 case 3: // Set X Property
618 _SysDebug("TODO: \\e]3; Support X properties");
619 st->Mode = MODE_STRING;
620 st->StringType = STRING_IGNORE;
622 case 4: // Change colour number
623 case 5: // Change special colour number
624 _SysDebug("TODO: \\e]%i;c; Support X properties", Ps);
625 st->Mode = MODE_STRING;
626 st->StringType = STRING_IGNORE;
628 case 10: // Change foreground to Pt
629 case 11: // Change background to Pt
631 // TODO: Can be Pt = [cps01234567]*;<str>
632 // > Clipboard data in base-64, cut/primary/select buffers 0-7
633 // > <str>='?' returns the clipbard in the same format
635 _SysDebug("Unknown VT100 OSC \\e]%i;", Ps);
636 st->Mode = MODE_STRING;
637 st->StringType = STRING_IGNORE;