From b170ee98e1649cc542e70f20547234192a0a4e69 Mon Sep 17 00:00:00 2001 From: nick black Date: Tue, 6 Jul 2021 20:30:16 -0400 Subject: [PATCH] you do not get to win https://www.youtube.com/watch?v=ASacQYJWOPI --- src/lib/input.c | 62 +++++++++++++++++++++++++++++++++++++++++++--- src/lib/termdesc.c | 42 ++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/lib/input.c b/src/lib/input.c index 46103c205..9db3bace5 100644 --- a/src/lib/input.c +++ b/src/lib/input.c @@ -633,6 +633,8 @@ typedef enum { 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_SDA_VER, // secondary DA, got semi, reading to next semi + STATE_SDA_DRAIN, // drain secondary DA to 'c' STATE_DA, // primary DA (CSI ? ... c) OR XTSMGRAPHICS OR DECRPM STATE_DA_1, // got '1', XTSMGRAPHICS color registers or primary DA STATE_DA_1_SEMI, // got '1;' @@ -817,6 +819,32 @@ stash_string(query_state* inits){ return 0; } +// use the version extracted from Secondary Device Attributes, assuming that +// it is Alacritty (we ought check the specified terminfo database entry). +// Alacritty writes its crate version with each more significant portion +// multiplied by 100^{portion ID}, where major, minor, patch are 2, 1, 0. +// what happens when a component exceeds 99? who cares. support XTVERSION. +static char* +set_sda_version(query_state* inits){ + int maj, min, patch; + if(inits->numeric <= 0){ + return NULL; + } + maj = inits->numeric / 10000; + min = (inits->numeric % 10000) / 100; + patch = inits->numeric % 100; + if(maj >= 100 || min >= 100 || patch >= 100){ + return NULL; + } + // 3x components (two digits max each), 2x '.', NUL would suggest 9 bytes, + // but older gcc __builtin___sprintf_chk insists on 13. fuck it. FIXME. + char* buf = malloc(13); + if(buf){ + sprintf(buf, "%d.%d.%d", maj, min, patch); + } + return buf; +} + // FIXME ought implement the full Williams automaton // FIXME sloppy af in general // returns 1 after handling the Device Attributes response, 0 if more input @@ -880,7 +908,15 @@ pump_control_read(query_state* inits, unsigned char c){ if(c == '?'){ inits->state = STATE_DA; // could also be DECRPM/XTSMGRAPHICS }else if(c == '>'){ - inits->state = STATE_SDA; + // SDA yields up Alacritty's crate version, but it doesn't unambiguously + // identify Alacritty. If we've got any other version information, skip + // directly to STATE_SDA_DRAIN, rather than doing STATE_SDA_VER. + if(inits->qterm || inits->version){ + loginfo("Identified terminal already; ignoring DA2\n"); + inits->state = STATE_SDA_DRAIN; + }else{ + inits->state = STATE_SDA; + } }else if(isdigit(c)){ inits->numeric = 0; if(ruts_numeric(&inits->numeric, c)){ @@ -1018,11 +1054,31 @@ pump_control_read(query_state* inits, unsigned char c){ } inits->state = STATE_TDA2; if(ruts_string(inits, STATE_TDA1)){ - inits->state = STATE_DCS_DRAIN; + inits->state = STATE_DCS_DRAIN; // FIXME return -1? } inits->numeric = 0; break; case STATE_SDA: + if(c == ';'){ + inits->state = STATE_SDA_VER; + inits->numeric = 0; + }else if(c == 'c'){ + inits->state = STATE_NULL; + } + break; + case STATE_SDA_VER: + if(c == ';'){ + inits->state = STATE_SDA_DRAIN; + loginfo("Got DA2 Pv: %u\n", inits->numeric); + // if a version was set, we couldn't have arrived here + if((inits->version = set_sda_version(inits)) == NULL){ + return -1; + } + }else if(ruts_numeric(&inits->numeric, c)){ + return -1; + } + break; + case STATE_SDA_DRAIN: if(c == 'c'){ inits->state = STATE_NULL; } @@ -1234,7 +1290,7 @@ int ncinputlayer_init(tinfo* tcache, FILE* infp, queried_terminals_e* detected, query_state inits = { .tcache = tcache, .state = STATE_NULL, - .qterm = TERMINAL_UNKNOWN, + .qterm = *detected, .cursor_x = -1, .cursor_y = -1, }; diff --git a/src/lib/termdesc.c b/src/lib/termdesc.c index 11e0389f6..78f47f517 100644 --- a/src/lib/termdesc.c +++ b/src/lib/termdesc.c @@ -242,6 +242,14 @@ grow_esc_table(tinfo* ti, const char* tstr, escape_e esc, return 0; } +// Tertiary Device Attributes, necessary to identify VTE. +// https://vt100.net/docs/vt510-rm/DA3.html +// Replies with DCS ! | ... ST +#define TRIDEVATTR "\x1b[=c" + +// Primary Device Attributes, necessary to elicit a response from terminals +// which don't respond to other queries. All known terminals respond to DA1. +// https://vt100.net/docs/vt510-rm/DA1.html // Device Attributes; replies with (depending on decTerminalID resource): // ⇒ CSI ? 1 ; 2 c ("VT100 with Advanced Video Option") // ⇒ CSI ? 1 ; 0 c ("VT101 with No Options") @@ -252,22 +260,38 @@ grow_esc_table(tinfo* ti, const char* tstr, escape_e esc, // ⇒ CSI ? 6 2 ; Ps c ("VT220") // ⇒ CSI ? 6 3 ; Ps c ("VT320") // ⇒ CSI ? 6 4 ; Ps c ("VT420") +#define PRIDEVATTR "\x1b[c" + +// XTVERSION. Replies with DCS > | ... ST +#define XTVERSION "\x1b[>0q" + +// XTGETTCAP['TN'] (Terminal Name) +#define XTGETTCAPTN "\x1bP+q544e\x1b\\" + +// Secondary Device Attributes, necessary to get Alacritty's version. Since +// this doesn't uniquely identify a terminal, we ask it last, so that if any +// queries which *do* unambiguously identify a terminal have succeeded, this +// needn't be paid attention to. +// https://vt100.net/docs/vt510-rm/DA2.html +// Replies with CSI > 6 1 ; Pv ; [01] c +#define SECDEVATTR "\x1b[>c" // these three queries (terminated with a Primary Device Attributes, to which // all known terminals reply) hopefully can uniquely and unquestionably // identify the terminal to which we are talking. // FIXME Konsole doesn't currently handle TDA, so we have to consume the 0c // in our state machine =[ -#define IDQUERIES "\x1b[=c" /* Tertiary Device Attributes */ \ - "\x1b[>0q" /* XTVERSION */ \ - "\x1bP+q544e\x1b\\" /* XTGETTCAP['TN'] */ +#define IDQUERIES TRIDEVATTR \ + XTVERSION \ + XTGETTCAPTN \ + SECDEVATTR // query background, replies in X color https://www.x.org/releases/X11R7.7/doc/man/man7/X.7.xhtml#heading11 -#define CSI_BGQ "\e]11;?\e\\" +#define CSI_BGQ "\x1b]11;?\e\\" // FIXME ought be using the u7 terminfo string here, if it exists. the great // thing is, if we get a response to this, we know we can use it for u7! -#define DSRCPR "\e[6n" +#define DSRCPR "\x1b[6n" // check for Synchronized Update Mode support. the p is necessary, but Konsole // doesn't consume it, so we have to handle that in our state machine =[. @@ -285,7 +309,7 @@ grow_esc_table(tinfo* ti, const char* tstr, escape_e esc, "\x1b[?1;3;256S" /* try to set 256 cregs */ \ CREGSXTSM \ GEOMXTSM \ - "\x1b[c" /* Device Attributes */ + PRIDEVATTR // we send an XTSMGRAPHICS to set up 256 color registers (the most we can // currently take advantage of; we need at least 64 to use sixel at all. @@ -381,6 +405,12 @@ apply_term_heuristics(tinfo* ti, const char* termname, int fd, } if(qterm == TERMINAL_UNKNOWN){ match_termname(termname, &qterm); + // we pick up alacritty's version via a weird hack involving Secondary + // Device Attributes. if we're not alacritty, don't trust that version. + if(qterm != TERMINAL_ALACRITTY){ + free(ti->termversion); + ti->termversion = NULL; + } } // st had neithercaps.sextants nor caps.quadrants last i checked (0.8.4) ti->caps.braille = true; // most everyone has working caps.braille, even from fonts