X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=Usermode%2FApplications%2Fgui_terminal_src%2Fvt100.c;h=6a2321ea7e91f88168ef81e50e333434229f7297;hb=98bd9c0c8985c50c42231c116a4e18fedd47761e;hp=4bc0495a80c9de7341ebb39c59b4176c9d93dc63;hpb=4493cf58800c6c9b145f07551922fdbd030d408d;p=tpg%2Facess2.git diff --git a/Usermode/Applications/gui_terminal_src/vt100.c b/Usermode/Applications/gui_terminal_src/vt100.c index 4bc0495a..6a2321ea 100644 --- a/Usermode/Applications/gui_terminal_src/vt100.c +++ b/Usermode/Applications/gui_terminal_src/vt100.c @@ -5,101 +5,212 @@ * vt100.c * - VT100/xterm Emulation */ -#include #include #include "include/vt100.h" #include "include/display.h" #include // isalpha -#include // _SysDebug -#include +#ifdef KERNEL_VERSION +# define _SysDebug(v...) Debug("VT100 "v) +#else +# include // _SysDebug +# include +# include +# include // malloc/free + +# define ASSERTC(a, r, b) assert(a r b) + +static inline int MIN(int a, int b) +{ + return a < b ? a : b; +} +#endif + +enum eExcapeMode { + MODE_NORMAL, + MODE_IGNORE, + MODE_STRING +}; +enum eStringType { + STRING_IGNORE, + STRING_TITLE +}; + +#define FLAG_BOLD 0x01 +#define FLAG_REVERSE 0x02 + +#define MAX_VT100_ESCAPE_LEN 32 +typedef struct { + uint32_t Flags; + int CurFG, CurBG; + + char cache[MAX_VT100_ESCAPE_LEN]; + int cache_len; + + enum eExcapeMode Mode; + + enum eStringType StringType; + size_t StringLen; + char *StringCache; +} tVT100State; const uint32_t caVT100Colours[] = { - // Black, Red, Green, Yellow, Blue, Purple, Cyan, Gray + // Black, Red, Green, Yellow, Blue, Magenta, Cyan, Gray // Same again, but bright - 0x000000, 0x770000, 0x007700, 0x777700, 0x000077, 0x770077, 0x007777, 0xAAAAAAA, - 0xCCCCCC, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFFF + 0x000000, 0x770000, 0x007700, 0x777700, 0x000077, 0x770077, 0x007777, 0xAAAAAA, + 0xCCCCCC, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF, }; + int _locate_eos(size_t Len, const char *Buf); + int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf); int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf); + int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf); -static inline int min(int a, int b) +int _locate_eos(size_t Len, const char *Buf) { - return a < b ? a : b; + for( size_t ret = 0; ret < Len; ret ++ ) + { + if( Buf[ret] == '\007' ) + return ret; + if( Buf[ret] == '\x9c' ) + return ret; + if( ret+1 < Len && Buf[ret] == '\x1b' && Buf[ret+1] == '\\' ) + return ret; + } + return -1; } +/** + * \brief Detect and handle VT100/ANSI/xterm escape sequences + * \param Term Terminal handle (opaque) + * \param Len Number of avaliable bytes + * \param Buf Input buffer (\a Len bytes long) + * \return -ve : Number of bytes that should be sent to screen + * \return +ve/0 : Number of bytes consumed by this function + */ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf) { - #define MAX_VT100_ESCAPE_LEN 16 - static char inc_buf[MAX_VT100_ESCAPE_LEN]; - static int inc_len = 0; + tVT100State *st = Display_GetTermState(Term); + + if( st == NULL ) { + st = malloc( sizeof(*st) ); + memset(st, 0, sizeof(*st)); + Display_SetTermState(Term, st); + } - if( inc_len > 0 || *Buf == '\x1b' ) + if( st->Mode == MODE_IGNORE ) { + st->Mode = MODE_NORMAL; + // Used for multi-byte EOS + _SysDebug("Ignore 1 '%c'", *Buf); + return 1; + } + else if( st->Mode == MODE_STRING ) + { + // We're in a string mode + int pos = _locate_eos(Len, Buf); + size_t bytes = (pos >= 0 ? pos : Len); + char *tmp = realloc(st->StringCache, st->StringLen + bytes+1); + if(!tmp) return bytes; + st->StringCache = tmp; + memcpy(tmp+st->StringLen, Buf, bytes); + tmp[st->StringLen+bytes] = 0; + st->StringLen += bytes; + + _SysDebug("pos=%i", pos); + _SysDebug("Buf[+%zi] = '%.*s'", bytes, bytes, Buf); + // Only apply when we hit EOS at the start + if( pos != 0 ) + return bytes; + switch(st->StringType) + { + case STRING_TITLE: + Display_SetTitle(Term, st->StringCache); + break; + case STRING_IGNORE: + break; + } + free(st->StringCache); + st->StringCache = 0; + st->StringLen = 0; + + if( *Buf == '\x1b' ) { + st->Mode = MODE_IGNORE; + // skip the '\\' + } + else + st->Mode = MODE_NORMAL; + return 1; + } + else { + // fall through + } + + if( st->cache_len > 0 || *Buf == '\x1b' ) { // Handle VT100 (like) escape sequence - int new_bytes = min(MAX_VT100_ESCAPE_LEN - inc_len, Len); + int new_bytes = MIN(MAX_VT100_ESCAPE_LEN - st->cache_len, Len); int ret = 0; - int old_inc_len = inc_len; - memcpy(inc_buf + inc_len, Buf, new_bytes); + int old_inc_len = st->cache_len; + + memcpy(st->cache + st->cache_len, Buf, new_bytes); if( new_bytes == 0 ) { - inc_len = 0; + _SysDebug("Term_HandleVT100: Hit max? (Len=%i) Flushing cache", Len); + st->cache_len = 0; return 0; } - inc_len += new_bytes; - //_SysDebug("inc_buf = %i '%.*s'", inc_len, inc_len, inc_buf); + st->cache_len += new_bytes; + assert(st->cache_len > old_inc_len); - if( inc_len <= 1 ) + if( st->cache_len <= 1 ) return 1; // Skip 1 character (the '\x1b') - switch(inc_buf[1]) - { - case '[': // Multibyte, funtime starts - ret = Term_HandleVT100_Long(Term, inc_len-2, inc_buf+2); - if( ret > 0 ) { - ret += 2; - } - break; - case 'D': - Display_ScrollDown(Term, 1); - ret = 2; - break; - case 'M': - Display_ScrollDown(Term, -1); - ret = 2; - break; - default: - ret = 2; - break; - } + ret = Term_HandleVT100_Short(Term, st->cache_len, st->cache); if( ret != 0 ) { - inc_len = 0; - assert(ret > old_inc_len); + // Check that we actually used the new data (as should have happened) + if( ret <= old_inc_len ) { + _SysDebug("Term_HandleVT100: ret(%i) <= old_inc_len(%i), inc_len=%i, '%*C'", + ret, old_inc_len, st->cache_len, st->cache_len, st->cache); + ASSERTC(ret, >, old_inc_len); + } + st->cache_len = 0; //_SysDebug("%i bytes of escape code '%.*s' (return %i)", // ret, ret, inc_buf, ret-old_inc_len); ret -= old_inc_len; // counter cached bytes } - else + else { ret = new_bytes; + _SysDebug("Term_HandleVT100: Caching %i bytes '%*C'", ret, ret, st->cache); + } return ret; } switch( *Buf ) { + case '\a': + // Alarm, aka bell + //Display_SoundBell(Term); + break; case '\b': + // backspace is aprarently just supposed to cursor left (if possible) Display_MoveCursor(Term, 0, -1); - Display_AddText(Term, 1, " "); - Display_MoveCursor(Term, 0, -1); - // TODO: Need to handle \t and ^A-Z return 1; case '\t': // TODO: tab (get current cursor pos, space until multiple of 8) + _SysDebug("TODO: VT100 Support \\t tab"); + Display_AddText(Term, 1, "\t"); // pass the buck for now return 1; case '\n': + // TODO: Support disabling CR after NL Display_Newline(Term, 1); return 1; case '\r': + if( Len >= 2 && Buf[1] == '\n' ) { + // Fast case for \r\n + Display_Newline(Term, 1); + return 2; + } Display_MoveCursor(Term, 0, INT_MIN); return 1; } @@ -126,8 +237,104 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf) return -ret; } +/** + * \brief Handle an escape code beginning with '\x1b' + * \return 0 : Insufficient data in buffer, wait for more + * \return +ve : Number of bytes in escape sequence + */ +int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf) +{ + int tmp; + switch(Buf[1]) + { + case '[': // Multibyte, funtime starts + tmp = Term_HandleVT100_Long(Term, Len-2, Buf+2); + assert(tmp >= 0); + if( tmp == 0 ) + return 0; + return tmp + 2; + case ']': + tmp = Term_HandleVT100_OSC(Term, Len-2, Buf+2); + assert(tmp >= 0); + if( tmp == 0 ) + return 0; + return tmp + 2; + + case '#': + if( Len == 2 ) + return 0; + switch(Buf[2]) + { + case 8: + _SysDebug("TODO \\e#%c DECALN - Fill screen with 'E'", Buf[2]); + break; + default: + _SysDebug("Unknown \\e#%c", Buf[2]); + break; + } + return 3; + + case '=': + _SysDebug("TODO: \\e= Application Keypad"); + return 2; + case '>': + _SysDebug("TODO: \\e= Normal Keypad"); + return 2; + + case '(': // Update G0 charset + tmp = 0; if(0) + case ')': // Update G1 charset + tmp = 1; if(0) + case '*': + tmp = 2; if(0) + case '+': + tmp = 3; + + if( Len <= 2 ) + return 0; // We need more + switch(Buf[2]) + { + case '0': // DEC Special Character/Linedrawing set + case 'A': // UK + case 'B': // US ASCII + break; + } + return 3; + case '7': + // Save cursor + Display_SaveCursor(Term); + return 2; + case '8': + // Restore cursor + Display_RestoreCursor(Term); + return 2; + case 'D': + // Cursor down, if at bottom scroll + Display_MoveCursor(Term, 1, 0); + // TODO: Scroll if at bottom (impl in _MoveCursor) + return 2; + case 'E': + Display_MoveCursor(Term, 1, INT_MIN); + // TODO: Scroll if at bottom (impl in _MoveCursor) + return 2; + case 'M': + // Cursor up, scroll if at top + Display_MoveCursor(Term, -1, 0); + return 2; + default: + _SysDebug("Unknown VT100 \\e%c", Buf[1]); + return 2; + } +} + +/** + * \brief Handle CSI escape sequences '\x1b[' + * \return 0 : insufficient data + * \return +ve : Number of bytes consumed + */ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer) { + tVT100State *st = Display_GetTermState(Term); char c; int argc = 0, j = 0; int args[6] = {0,0,0,0,0,0}; @@ -136,13 +343,16 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer) // Get Arguments if(j == Len) return 0; c = Buffer[j++]; - if(c == '?') { + if(c == '?') + { bQuestionMark = 1; if(j == Len) return 0; c = Buffer[j++]; } - if( '0' <= c && c <= '9' ) + if( ('0' <= c && c <= '9') || c == ';' ) { + if(c == ';') + argc ++; do { if(c == ';') { if(j == Len) return 0; @@ -177,12 +387,39 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer) case 'l': // unset switch(args[0]) { + case 1: // Aplication cursor keys + _SysDebug("TODO: \\e[?1%c Application cursor keys", c); + break; + case 3: // 132 Column mode + _SysDebug("TODO: \\e[?3%c 132 Column mode", c); + break; + case 4: // Smooth (Slow) Scroll + _SysDebug("TODO: \\e[?4%c Smooth (Slow) Scroll", c); + break; + case 5: // Reverse Video + _SysDebug("TODO: \\e[?5%c Reverse Video", c); + break; + case 6: // Origin Mode + _SysDebug("TODO: \\e[?6%c Origin Mode", c); + break; + case 12: + //_SysDebug("TODO: \\e[?25%c Start/Stop blinking cursor", c); + //Display_SolidCursor(Term, !set); + break; case 25: // Hide cursor - _SysDebug("TODO: \\e[?25%c Show/Hide cursor", c); + //_SysDebug("TODO: \\e[?25%c Show/Hide cursor", c); + //Display_ShowCursor(Term, set); break; case 1047: // Alternate buffer Display_ShowAltBuffer(Term, set); break; + case 1048: // Save/restore cursor in DECSC + _SysDebug("TODO: \\e[?1048%c Save/Restore cursor", c); + break; + case 1049: // Save/restore cursor in DECSC and use alternate buffer + _SysDebug("TODO: \\e[?1049%c Save/Restore cursor", c); + Display_ShowAltBuffer(Term, set); + break; default: _SysDebug("TODO: \\e[?%i%c Unknow DEC private mode", args[0], c); break; @@ -198,54 +435,209 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer) // Standard commands switch( c ) { + case 'A': + Display_MoveCursor(Term, -(args[0] != 0 ? args[0] : 1), 0); + break; + case 'B': + Display_MoveCursor(Term, (args[0] != 0 ? args[0] : 1), 0); + break; + case 'C': + Display_MoveCursor(Term, 0, (args[0] != 0 ? args[0] : 1)); + break; + case 'D': + Display_MoveCursor(Term, 0, -(args[0] != 0 ? args[0] : 1)); + break; case 'H': - if( argc != 2 ) { + if( argc == 0 ) { + Display_SetCursor(Term, 0, 0); } - else { - Display_SetCursor(Term, args[0], args[1]); + else if( argc == 1 ) { + Display_SetCursor(Term, args[0]-1, 0); + } + else if( argc == 2 ) { + // Adjust 1-based cursor position to 0-based + Display_SetCursor(Term, args[0]-1, args[1]-1); } break; - case 'J': - if( argc == 0 ) + case 'J': // Clear lines + switch( args[0] ) + { + case 0: + Display_ClearLines(Term, 1); // Down + break; + case 1: + Display_ClearLines(Term, -1); // Up + break; + case 2: + Display_ClearLines(Term, 0); // All + break; + default: + _SysDebug("Unknown VT100 %i J", args[0]); + break; + } + break; + case 'K': + switch( args[0] ) + { + case 0: // To EOL + Display_ClearLine(Term, 1); + break; + case 1: // To SOL + Display_ClearLine(Term, -1); + break; + case 2: Display_ClearLine(Term, 0); - else if( args[0] == 2 ) - Display_ClearLines(Term, 0); // Entire screen! - else - _SysDebug("TODO: VT100 %i J", args[0]); + break; + default: + _SysDebug("Unknown VT100 %i K", args[0]); + break; + } + break; + case 'S': // Scroll text up n=1 (expose bottom) + Display_ScrollDown(Term, -(argc >= 1 ? args[0] : 1)); break; - case 'T': // Scroll down n=1 - Display_ScrollDown(Term, 1); + case 'T': // Scroll text down n=1 (expose top) + Display_ScrollDown(Term, (argc >= 1 ? args[0] : 1)); + break; + case 'c': // Send Device Attributes + switch(args[0]) + { + case 0: // Request attributes from terminal + // "VT100 with Advanced Video Option" (same as screen returns) + Display_SendInput(Term, "\x1b[?1;2c"); + break; + default: + _SysDebug("TODO: Request device attributes \\e[%ic", args[0]); + break; + } + break; + case 'f': + if( argc != 2 ) { + Display_SetCursor(Term, 0, 0); + } + else { + // Adjust 1-based cursor position to 0-based + Display_SetCursor(Term, args[0]-1, args[1]-1); + } + break; + case 'h': + case 'l': + for( int i = 0; i < argc; i ++ ) + { + switch(args[i]) + { + default: + _SysDebug("Unknown VT100 mode \e[%i%c", + args[i], c); + break; + } + } break; case 'm': if( argc == 0 ) { // Reset + Display_ResetAttributes(Term); + } + else if( args[0] == 48 ) + { + // ISO-8613-3 Background + if( args[1] == 2 ) { + uint32_t col = 0; + col |= (uint32_t)args[2] << 16; + col |= (uint32_t)args[3] << 8; + col |= (uint32_t)args[4] << 0; + Display_SetBackground(Term, col); + } + else if( args[1] == 5 ) { + _SysDebug("TODO: Support xterm palette BG %i", args[2]); + } + else { + _SysDebug("VT100 Unknown mode set \e[48;%im", args[1]); + } + } + else if( args[0] == 38 ) + { + // ISO-8613-3 Foreground + if( args[1] == 2 ) { + uint32_t col = 0; + col |= (uint32_t)args[2] << 16; + col |= (uint32_t)args[3] << 8; + col |= (uint32_t)args[4] << 0; + Display_SetForeground(Term, col); + } + else if( args[1] == 5 ) { + _SysDebug("TODO: Support xterm palette FG %i", args[2]); + } + else { + _SysDebug("VT100 Unknown mode set \e[38;%im", args[1]); + } } else { - int i; - for( i = 0; i < argc; i ++ ) + for( int i = 0; i < argc; i ++ ) { - if( args[i] < 8 ) - { - // TODO: Flags? - } - else if( 30 <= args[i] && args[i] <= 37 ) - { - // TODO: Bold/bright - Display_SetForeground( Term, caVT100Colours[ args[i]-30 ] ); - } - else if( 40 <= args[i] && args[i] <= 47 ) + switch(args[i]) { - // TODO: Bold/bright - Display_SetBackground( Term, caVT100Colours[ args[i]-30 ] ); + case 0: + st->Flags = 0; + Display_ResetAttributes(Term); + break; + case 1: + st->Flags |= FLAG_BOLD; + Display_SetForeground( Term, caVT100Colours[st->CurFG + 8] ); + break; + case 2: + _SysDebug("TODO: \\e[2m - Reverse"); + break; + case 4: + _SysDebug("TODO: \\e[4m - Underscore"); + break; + //case 5: + // _SysDebug("TODO: \\e[5m - Blink/bold"); + // break; + case 7: + _SysDebug("TODO: \\e[7m - Reverse"); + break; + case 24: // Not underlined + case 27: // Not inverse + break; + case 30 ... 37: + st->CurFG = args[i]-30; + if(0) + case 39: + st->CurFG = 7; + Display_SetForeground( Term, + caVT100Colours[ st->CurFG + (st->Flags&FLAG_BOLD?8:0) ] ); + break; + case 40 ... 47: + st->CurBG = args[i]-40; + if(0) + case 49: + st->CurBG = 0; + Display_SetBackground( Term, caVT100Colours[ st->CurBG ] ); + break; + case 90 ... 97: + st->CurFG = args[i]-90 + 8; + Display_SetForeground( Term, caVT100Colours[ st->CurBG ] ); + break;; + case 100 ... 107: + st->CurBG = args[i]-100 + 8; + Display_SetBackground( Term, caVT100Colours[ st->CurBG ] ); + break;; + default: + _SysDebug("Unknown mode set \\e[%im", args[i]); + break; } } } break; + // Device Status Report + case 'n': + break; // Set scrolling region case 'r': - Display_SetScrollArea(Term, args[0], args[1]); + Display_SetScrollArea(Term, args[0]-1, (args[1] - args[0])+1); break; case 's': @@ -261,3 +653,61 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer) } return j; } + +int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf) +{ + tVT100State *st = Display_GetTermState(Term); + + int ofs = 0; + // OSC Ps ; Pt [ST/BEL] + if(Len < 2) return 0; // Need moar + + int Ps = 0; + while( ofs < Len && isdigit(Buf[ofs]) ) { + Ps = Ps * 10 + (Buf[ofs] - '0'); + ofs ++; + } + + if( ofs == Len ) return 0; + if( Buf[ofs] != ';' ) { + // Error + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + return ofs; + } + ofs ++; + + switch(Ps) + { + case 0: // Icon Name + Window Title + case 1: // Icon Name + case 2: // Window Title + st->Mode = MODE_STRING; + st->StringType = STRING_TITLE; + break; + case 3: // Set X Property + _SysDebug("TODO: \\e]3; Support X properties"); + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + break; + case 4: // Change colour number + case 5: // Change special colour number + _SysDebug("TODO: \\e]%i;c; Support X properties", Ps); + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + break; + case 10: // Change foreground to Pt + case 11: // Change background to Pt + case 52: + // TODO: Can be Pt = [cps01234567]*; + // > Clipboard data in base-64, cut/primary/select buffers 0-7 + // > ='?' returns the clipbard in the same format + default: + _SysDebug("Unknown VT100 OSC \\e]%i;", Ps); + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + break; + } + return ofs; +} +