extract terminal query responses #1761

This commit is contained in:
nick black 2021-06-14 13:34:52 -04:00 committed by Nick Black
parent 1c6fae3403
commit b44f17f4ad
2 changed files with 83 additions and 41 deletions

View File

@ -610,41 +610,46 @@ void ncinputlayer_stop(ncinputlayer* nilayer){
input_free_esctrie(&nilayer->inputescapes); input_free_esctrie(&nilayer->inputescapes);
} }
typedef enum {
STATE_NULL,
STATE_ESC, // escape; aborts any active sequence
STATE_CSI, // control sequence introducer
STATE_DCS, // device control string
// XTVERSION replies with DCS > | ... ST
STATE_XTVERSION1,
STATE_XTVERSION2,
// XTGETTCAP replies with DCS 1 + r for a good request, or 0 + r for bad
STATE_XTGETTCAP1, // XTGETTCAP, got '0/1' (DCS 0/1 + r Pt ST)
STATE_XTGETTCAP2, // XTGETTCAP, got '+' (DCS 0/1 + r Pt ST)
STATE_XTGETTCAP3, // XTGETTCAP, got 'r' (DCS 0/1 + r Pt ST)
STATE_XTGETTCAP_TERMNAME1, // got property 544E, 'TN' (terminal name) first hex nibble
STATE_XTGETTCAP_TERMNAME2, // got property 544E, 'TN' (terminal name) second hex nibble
STATE_DCS_DRAIN, // throw away input until we hit escape
STATE_TDA1, // tertiary DA, got '!'
STATE_TDA2, // tertiary DA, got '|', first hex nibble
STATE_TDA3, // tertiary DA, second hex nibble
STATE_SDA, // secondary DA (CSI > Pp ; Pv ; Pc c)
STATE_DA, // primary DA (CSI ? ... c) OR XTSMGRAPHICS
STATE_DA_1, // got '1', XTSMGRAPHICS color registers or primary DA
STATE_DA_1_SEMI, // got '1;'
STATE_DA_1_0, // got '1;0', XTSMGRAPHICS color registers or VT101
STATE_DA_6, // got '6', could be VT102 or VT220/VT320/VT420
STATE_DA_DRAIN, // drain out the primary DA to 'c'
STATE_SIXEL,// XTSMGRAPHICS about Sixel geometry (got '2')
STATE_SIXEL_SEMI1, // got first semicolon in sixel geometry, want Ps
STATE_SIXEL_SUCCESS, // got Ps == 0, want second semicolon
STATE_SIXEL_WIDTH, // reading maximum sixel width until ';'
STATE_SIXEL_HEIGHT, // reading maximum sixel height until 'S'
STATE_SIXEL_CREGS, // reading max color registers until 'S'
STATE_XTSMGRAPHICS_DRAIN, // drain out XTSMGRAPHICS to 'S'
} initstates_e;
typedef struct init_state { typedef struct init_state {
tinfo* tcache; tinfo* tcache;
enum { queried_terminals_e qterm; // discovered terminal
STATE_NULL, initstates_e state, stringstate;
STATE_ESC, // escape; aborts any active sequence // stringstate is the state at which this string was initialized, and can be
STATE_CSI, // control sequence introducer // one of STATE_XTVERSION1, STATE_XTGETTCAP_TERMNAME1, STATE_TDA1,
STATE_DCS, // device control string
// XTVERSION replies with DCS > | ... ST
STATE_XTVERSION1,
STATE_XTVERSION2,
// XTGETTCAP replies with DCS 1 + r for a good request, or 0 + r for bad
STATE_XTGETTCAP1, // XTGETTCAP, got '0/1' (DCS 0/1 + r Pt ST)
STATE_XTGETTCAP2, // XTGETTCAP, got '+' (DCS 0/1 + r Pt ST)
STATE_XTGETTCAP3, // XTGETTCAP, got 'r' (DCS 0/1 + r Pt ST)
STATE_XTGETTCAP_TERMNAME1, // got property 544E, 'TN' (terminal name) first hex nibble
STATE_XTGETTCAP_TERMNAME2, // got property 544E, 'TN' (terminal name) second hex nibble
STATE_DCS_DRAIN, // throw away input until we hit escape
STATE_TDA1, // tertiary DA, got '!'
STATE_TDA2, // tertiary DA, got '|', first hex nibble
STATE_TDA3, // tertiary DA, second hex nibble
STATE_SDA, // secondary DA (CSI > Pp ; Pv ; Pc c)
STATE_DA, // primary DA (CSI ? ... c) OR XTSMGRAPHICS
STATE_DA_1, // got '1', XTSMGRAPHICS color registers or primary DA
STATE_DA_1_SEMI, // got '1;'
STATE_DA_1_0, // got '1;0', XTSMGRAPHICS color registers or VT101
STATE_DA_6, // got '6', could be VT102 or VT220/VT320/VT420
STATE_DA_DRAIN, // drain out the primary DA to 'c'
STATE_SIXEL,// XTSMGRAPHICS about Sixel geometry (got '2')
STATE_SIXEL_SEMI1, // got first semicolon in sixel geometry, want Ps
STATE_SIXEL_SUCCESS, // got Ps == 0, want second semicolon
STATE_SIXEL_WIDTH, // reading maximum sixel width until ';'
STATE_SIXEL_HEIGHT, // reading maximum sixel height until 'S'
STATE_SIXEL_CREGS, // reading max color registers until 'S'
STATE_XTSMGRAPHICS_DRAIN, // drain out XTSMGRAPHICS to 'S'
} state;
int numeric; // currently-lexed numeric int numeric; // currently-lexed numeric
char runstring[80]; // running string char runstring[80]; // running string
size_t stridx; // position to write in string size_t stridx; // position to write in string
@ -690,7 +695,7 @@ ruts_hex(int* numeric, unsigned char c){
// add a decoded hex byte to the string // add a decoded hex byte to the string
static int static int
ruts_string(init_state* inits){ ruts_string(init_state* inits, initstates_e state){
if(inits->stridx == sizeof(inits->runstring)){ if(inits->stridx == sizeof(inits->runstring)){
return -1; // overflow, too long return -1; // overflow, too long
} }
@ -701,18 +706,51 @@ ruts_string(init_state* inits){
if(!isprint(c)){ if(!isprint(c)){
return -1; return -1;
} }
inits->stringstate = state;
inits->runstring[inits->stridx] = c; inits->runstring[inits->stridx] = c;
inits->runstring[++inits->stridx] = '\0'; inits->runstring[++inits->stridx] = '\0';
return 0; return 0;
} }
static int
stash_string(init_state* inits){
fprintf(stderr, "string terminator after %d [%s]\n", inits->stringstate, inits->runstring);
switch(inits->stringstate){
case STATE_XTVERSION1:{
int xversion;
if(sscanf(inits->runstring, "XTerm(%d)", &xversion) == 1){
inits->qterm = TERMINAL_XTERM;
}
break;
}case STATE_XTGETTCAP_TERMNAME1:
if(strcmp(inits->runstring, "xterm-kitty") == 0){
inits->qterm = TERMINAL_KITTY;
}else if(strcmp(inits->runstring, "mlterm") == 0){
inits->qterm = TERMINAL_MLTERM;
}
break;
case STATE_TDA1:
if(strcmp(inits->runstring, "~VTE") == 0){
inits->qterm = TERMINAL_VTE;
}else if(strcmp(inits->runstring, "\x1bP!|464f4f54\x1b\\") == 0){
inits->qterm = TERMINAL_FOOT;
}
break;
default:
break;
}
inits->runstring[0] = '\0';
inits->stridx = 0;
return 0;
}
// FIXME ought implement the full Williams automaton // FIXME ought implement the full Williams automaton
// FIXME doesn't handle 8-bit controls (would need convert UTF-8) // FIXME doesn't handle 8-bit controls (would need convert UTF-8)
// returns 1 after handling the Device Attributes response, 0 if more input // returns 1 after handling the Device Attributes response, 0 if more input
// ought be fed to the machine, and -1 on an invalid state transition. // ought be fed to the machine, and -1 on an invalid state transition.
static int static int
pump_control_read(init_state* inits, unsigned char c){ pump_control_read(init_state* inits, unsigned char c){
fprintf(stderr, "state: %2d char: %1c %3d %02x\n", inits->state, isprint(c) ? c : ' ', c, c); //fprintf(stderr, "state: %2d char: %1c %3d %02x\n", inits->state, isprint(c) ? c : ' ', c, c);
if(c == NCKEY_ESC){ if(c == NCKEY_ESC){
/*if(inits->state != STATE_NULL && inits->state != STATE_DCS && inits->state != STATE_DCS_DRAIN && inits->state != STATE_XTVERSION2 && inits->state != STATE_XTGETTCAP3 && inits->state != STATE_DA_DRAIN){ /*if(inits->state != STATE_NULL && inits->state != STATE_DCS && inits->state != STATE_DCS_DRAIN && inits->state != STATE_XTVERSION2 && inits->state != STATE_XTGETTCAP3 && inits->state != STATE_DA_DRAIN){
fprintf(stderr, "Unexpected escape in state %d\n", inits->state); fprintf(stderr, "Unexpected escape in state %d\n", inits->state);
@ -731,7 +769,9 @@ fprintf(stderr, "state: %2d char: %1c %3d %02x\n", inits->state, isprint(c) ? c
}else if(c == 'P'){ }else if(c == 'P'){
inits->state = STATE_DCS; inits->state = STATE_DCS;
}else if(c == '\\'){ }else if(c == '\\'){
fprintf(stderr, "string terminator after [%s]\n", inits->runstring); if(stash_string(inits)){
return -1;
}
inits->state = STATE_NULL; inits->state = STATE_NULL;
} }
break; break;
@ -776,7 +816,7 @@ fprintf(stderr, "string terminator after [%s]\n", inits->runstring);
break; break;
case STATE_XTVERSION2: case STATE_XTVERSION2:
inits->numeric = c; inits->numeric = c;
if(ruts_string(inits)){ if(ruts_string(inits, STATE_XTVERSION1)){
return -1; return -1;
} }
break; break;
@ -819,7 +859,7 @@ fprintf(stderr, "string terminator after [%s]\n", inits->runstring);
return -1; return -1;
} }
inits->state = STATE_XTGETTCAP_TERMNAME1; inits->state = STATE_XTGETTCAP_TERMNAME1;
if(ruts_string(inits)){ if(ruts_string(inits, STATE_XTGETTCAP_TERMNAME1)){
return -1; return -1;
} }
inits->numeric = 0; inits->numeric = 0;
@ -844,7 +884,7 @@ fprintf(stderr, "string terminator after [%s]\n", inits->runstring);
return -1; return -1;
} }
inits->state = STATE_TDA2; inits->state = STATE_TDA2;
if(ruts_string(inits)){ if(ruts_string(inits, STATE_TDA1)){
inits->state = STATE_DCS_DRAIN; inits->state = STATE_DCS_DRAIN;
} }
inits->numeric = 0; inits->numeric = 0;
@ -985,6 +1025,7 @@ control_read(tinfo* tcache, int ttyfd){
init_state inits = { init_state inits = {
.tcache = tcache, .tcache = tcache,
.state = STATE_NULL, .state = STATE_NULL,
.qterm = TERMINAL_UNKNOWN,
}; };
unsigned char* buf; unsigned char* buf;
ssize_t s; ssize_t s;
@ -997,6 +1038,7 @@ control_read(tinfo* tcache, int ttyfd){
int r = pump_control_read(&inits, buf[idx]); int r = pump_control_read(&inits, buf[idx]);
if(r == 1){ // success! if(r == 1){ // success!
free(buf); free(buf);
fprintf(stderr, "at end, derived terminal %d\n", inits.qterm);
return 0; return 0;
}else if(r < 0){ }else if(r < 0){
goto err; goto err;

View File

@ -167,7 +167,7 @@ term_supported_styles(const tinfo* ti){
// if the terminal unambiguously identifies itself in response to our // if the terminal unambiguously identifies itself in response to our
// queries, go ahead and trust that, overriding TERM. // queries, go ahead and trust that, overriding TERM.
enum queried_terminal { typedef enum {
TERMINAL_UNKNOWN, // no useful information from queries; use termname TERMINAL_UNKNOWN, // no useful information from queries; use termname
TERMINAL_LINUX, // ioctl()s TERMINAL_LINUX, // ioctl()s
TERMINAL_XTERM, // XTVERSION == 'XTerm(ver)' TERMINAL_XTERM, // XTVERSION == 'XTerm(ver)'
@ -176,7 +176,7 @@ enum queried_terminal {
TERMINAL_FOOT, // TDA: "\EP!|464f4f54\E\\" TERMINAL_FOOT, // TDA: "\EP!|464f4f54\E\\"
TERMINAL_MLTERM, // XTGETTCAP['TN'] == 'mlterm' TERMINAL_MLTERM, // XTGETTCAP['TN'] == 'mlterm'
TERMINAL_WEZTERM, TERMINAL_WEZTERM,
}; } queried_terminals_e;
#ifdef __cplusplus #ifdef __cplusplus
} }