#include <string.h>
#include "tty.h"
static code_t esc_decode_vt(uint32_t vt_code ) {
switch(vt_code) {
case 1: return KEY_HOME;
case 2: return KEY_INS;
case 3: return KEY_DEL;
case 4: return KEY_END;
case 5: return KEY_PAGEUP;
case 6: return KEY_PAGEDOWN;
case 7: return KEY_HOME;
case 8: return KEY_END;
default:
if (vt_code >= 10 && vt_code <= 15) { return KEY_F(1 + (vt_code - 10)); }
if (vt_code == 16) { return KEY_F5; } if (vt_code >= 17 && vt_code <= 21) { return KEY_F(6 + (vt_code - 17)); }
if (vt_code >= 23 && vt_code <= 26) { return KEY_F(11 + (vt_code - 23)); }
if (vt_code >= 28 && vt_code <= 29) { return KEY_F(15 + (vt_code - 28)); }
if (vt_code >= 31 && vt_code <= 34) { return KEY_F(17 + (vt_code - 31)); }
}
return KEY_NONE;
}
static code_t esc_decode_xterm( uint8_t xcode ) {
switch(xcode) {
case 'A': return KEY_UP;
case 'B': return KEY_DOWN;
case 'C': return KEY_RIGHT;
case 'D': return KEY_LEFT;
case 'E': return '5'; case 'F': return KEY_END;
case 'H': return KEY_HOME;
case 'Z': return KEY_TAB | KEY_MOD_SHIFT;
case 'I': return KEY_PAGEUP;
case 'L': return KEY_INS;
case 'M': return KEY_F1;
case 'N': return KEY_F2;
case 'O': return KEY_F3;
case 'P': return KEY_F4; case 'Q': return KEY_F5;
case 'R': return KEY_F6;
case 'S': return KEY_F7;
case 'T': return KEY_F8;
case 'U': return KEY_PAGEDOWN; case 'V': return KEY_PAGEUP; case 'W': return KEY_F11;
case 'X': return KEY_F12;
case 'Y': return KEY_END; }
return KEY_NONE;
}
static code_t esc_decode_ss3( uint8_t ss3_code ) {
switch(ss3_code) {
case 'A': return KEY_UP;
case 'B': return KEY_DOWN;
case 'C': return KEY_RIGHT;
case 'D': return KEY_LEFT;
case 'E': return '5'; case 'F': return KEY_END;
case 'H': return KEY_HOME;
case 'I': return KEY_TAB;
case 'Z': return KEY_TAB | KEY_MOD_SHIFT;
case 'M': return KEY_LINEFEED;
case 'P': return KEY_F1;
case 'Q': return KEY_F2;
case 'R': return KEY_F3;
case 'S': return KEY_F4;
case 'T': return KEY_F5;
case 'U': return KEY_F6;
case 'V': return KEY_F7;
case 'W': return KEY_F8;
case 'X': return KEY_F9; case 'Y': return KEY_F10;
case 'a': return KEY_UP;
case 'b': return KEY_DOWN;
case 'c': return KEY_RIGHT;
case 'd': return KEY_LEFT;
case 'j': return '*';
case 'k': return '+';
case 'l': return ',';
case 'm': return '-';
case 'n': return KEY_DEL; case 'o': return '/';
case 'p': return KEY_INS;
case 'q': return KEY_END;
case 'r': return KEY_DOWN;
case 's': return KEY_PAGEDOWN;
case 't': return KEY_LEFT;
case 'u': return '5';
case 'v': return KEY_RIGHT;
case 'w': return KEY_HOME;
case 'x': return KEY_UP;
case 'y': return KEY_PAGEUP;
}
return KEY_NONE;
}
static void tty_read_csi_num(tty_t* tty, uint8_t* ppeek, uint32_t* num, long esc_timeout) {
*num = 1; ssize_t count = 0;
uint32_t i = 0;
while (*ppeek >= '0' && *ppeek <= '9' && count < 16) {
uint8_t digit = *ppeek - '0';
if (!tty_readc_noblock(tty,ppeek,esc_timeout)) { break; } count++;
i = 10*i + digit;
}
if (count > 0) { *num = i; }
}
static code_t tty_read_csi(tty_t* tty, uint8_t c1, uint8_t peek, code_t mods0, long esc_timeout) {
if (c1 == '[' && strchr("[Oo", (char)peek) != NULL) {
uint8_t cx = peek;
if (tty_readc_noblock(tty,&peek,esc_timeout)) {
c1 = cx;
}
}
uint8_t special = 0;
if (strchr(":<=>?",(char)peek) != NULL) {
special = peek;
if (!tty_readc_noblock(tty,&peek,esc_timeout)) {
tty_cpush_char(tty,special); return (key_unicode(c1) | KEY_MOD_ALT); }
}
uint32_t num1 = 1;
uint32_t num2 = 1;
tty_read_csi_num(tty,&peek,&num1,esc_timeout);
if (peek == ';') {
if (!tty_readc_noblock(tty,&peek,esc_timeout)) { return KEY_NONE; }
tty_read_csi_num(tty,&peek,&num2,esc_timeout);
}
uint8_t final = peek;
code_t modifiers = mods0;
debug_msg("tty: escape sequence: ESC %c %c %d;%d %c\n", c1, (special == 0 ? '_' : special), num1, num2, final);
if ((final == '@' || final == '9') && c1 == '[' && num1 == 1) {
if (final == '@') { num1 = 3; } else if (final == '9') { num1 = 2; } final = '~';
}
else if (final == '^' || final == '$' || final == '@') {
if (final == '^') { modifiers |= KEY_MOD_CTRL; }
if (final == '$') { modifiers |= KEY_MOD_SHIFT; }
if (final == '@') { modifiers |= KEY_MOD_SHIFT | KEY_MOD_CTRL; }
final = '~';
}
else if (c1 == '[' && final >= 'a' && final <= 'd') { modifiers |= KEY_MOD_SHIFT;
final = 'A' + (final - 'a');
}
if (((c1 == 'O') || (c1 == '[' && final != '~' && final != 'u')) &&
(num2 == 1 && num1 > 1 && num1 <= 8))
{
num2 = num1;
num1 = 1;
}
if (num2 > 1 && num2 <= 9) {
if (num2 == 9) { num2 = 3; } num2--;
if (num2 & 0x1) { modifiers |= KEY_MOD_SHIFT; }
if (num2 & 0x2) { modifiers |= KEY_MOD_ALT; }
if (num2 & 0x4) { modifiers |= KEY_MOD_CTRL; }
}
code_t code = KEY_NONE;
if (final == '~') {
code = esc_decode_vt(num1);
}
else if (c1 == '[' && final == 'u') {
code = key_unicode(num1);
}
else if (c1 == 'O' && ((final >= 'A' && final <= 'Z') || (final >= 'a' && final <= 'z'))) {
code = esc_decode_ss3(final);
}
else if (num1 == 1 && final >= 'A' && final <= 'Z') {
code = esc_decode_xterm(final);
}
else if (c1 == '[' && final == 'R') {
code = KEY_NONE;
}
if (code == KEY_NONE && final != 'R') {
debug_msg("tty: ignore escape sequence: ESC %c %" PRIz "u;%" PRIz "u %c\n", c1, num1, num2, final);
}
return (code != KEY_NONE ? (code | modifiers) : KEY_NONE);
}
static code_t tty_read_osc( tty_t* tty, uint8_t* ppeek, long esc_timeout ) {
debug_msg("discard OSC response..\n");
while (true) {
uint8_t c = *ppeek;
if (c <= '\x07') { if (c != '\x07') { tty_cpush_char( tty, c ); }
break;
}
else if (c == '\x1B') {
uint8_t c1;
if (!tty_readc_noblock(tty, &c1, esc_timeout)) { break; }
if (c1 == '\\') { break; }
tty_cpush_char(tty,c1);
}
if (!tty_readc_noblock(tty, ppeek, esc_timeout)) { break; }
}
return KEY_NONE;
}
ic_private code_t tty_read_esc(tty_t* tty, long esc_initial_timeout, long esc_timeout) {
code_t mods = 0;
uint8_t peek = 0;
if (!tty_readc_noblock(tty, &peek, esc_initial_timeout)) { return KEY_ESC; }
if (peek == KEY_ESC) {
if (!tty_readc_noblock(tty, &peek, esc_timeout)) { goto alt; }
mods |= KEY_MOD_ALT;
}
if (peek == '[') {
if (!tty_readc_noblock(tty, &peek, esc_timeout)) { goto alt; }
return tty_read_csi(tty, '[', peek, mods, esc_timeout); }
if (peek == 'O' || peek == 'o' || peek == '?' ) {
uint8_t c1 = peek;
if (!tty_readc_noblock(tty, &peek, esc_timeout)) { goto alt; }
if (c1 == 'o') {
mods |= KEY_MOD_CTRL;
}
return tty_read_csi(tty,'O',peek,mods, esc_timeout); }
if (peek == ']') {
if (!tty_readc_noblock(tty, &peek, esc_timeout)) { goto alt; }
return tty_read_osc(tty, &peek, esc_timeout); }
alt:
return (key_unicode(peek) | KEY_MOD_ALT); }