mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
always send DSRCPR at init, use it to verify u7
for NCOPTION_PRESERVE_CURSOR, we want to know the location of the cursor at startup. go ahead and always send a DSRCPR in terminal interrogation. if we get a reply to it, hey, we just verified that the terminal knows DSRCPR. if we find a u7 value in terminfo that's different from the standard DSRCPR, we ought send that in addition. #1816
This commit is contained in:
parent
166212f234
commit
9f92986a6b
6
USAGE.md
6
USAGE.md
@ -106,6 +106,12 @@ typedef enum {
|
||||
// registration of these signal handlers.
|
||||
#define NCOPTION_NO_QUIT_SIGHANDLERS 0x0008
|
||||
|
||||
// Initialize the standard plane's virtual cursor to match the physical cursor
|
||||
// at context creation time. Together with NCOPTION_NO_ALTERNATE_SCREEN and a
|
||||
// scrolling standard plane, this facilitates easy scrolling-style programs in
|
||||
// rendered mode.
|
||||
#define NCOPTION_PRESERVE_CURSOR 0x0010ull
|
||||
|
||||
// Notcurses typically prints version info in notcurses_init() and performance
|
||||
// info in notcurses_stop(). This inhibits that output.
|
||||
#define NCOPTION_SUPPRESS_BANNERS 0x0020
|
||||
|
@ -117,7 +117,7 @@ tinfo_debug_styles(struct ncplane* n, const char* indent){
|
||||
// notcurses_render() without the alternate screen, no?
|
||||
int main(void){
|
||||
notcurses_options nopts = {
|
||||
.flags = NCOPTION_NO_ALTERNATE_SCREEN,
|
||||
.flags = NCOPTION_NO_ALTERNATE_SCREEN | NCOPTION_PRESERVE_CURSOR,
|
||||
};
|
||||
struct notcurses* nc = notcurses_init(&nopts, NULL);
|
||||
if(nc == NULL){
|
||||
|
@ -650,6 +650,8 @@ typedef enum {
|
||||
STATE_XTSMGRAPHICS_DRAIN, // drain out XTSMGRAPHICS to 'S'
|
||||
STATE_APPSYNC_REPORT, // got DECRPT ?2026
|
||||
STATE_APPSYNC_REPORT_DRAIN, // drain out decrpt to 'y'
|
||||
STATE_CURSOR, // reading row of cursor location to ';'
|
||||
STATE_CURSOR_COL, // reading col of cursor location to 'R'
|
||||
} initstates_e;
|
||||
|
||||
typedef struct query_state {
|
||||
@ -661,12 +663,13 @@ typedef struct query_state {
|
||||
// stringstate is the state at which this string was initialized, and can be
|
||||
// one of STATE_XTVERSION1, STATE_XTGETTCAP_TERMNAME1, STATE_TDA1, and STATE_BG1
|
||||
initstates_e state, stringstate;
|
||||
int numeric; // currently-lexed numeric
|
||||
char runstring[80]; // running string
|
||||
size_t stridx; // position to write in string
|
||||
uint32_t bg; // queried default background or 0
|
||||
bool xtgettcap_good; // high when we've received DCS 1
|
||||
bool appsync; // application-synchronized updates advertised
|
||||
int numeric; // currently-lexed numeric
|
||||
char runstring[80]; // running string
|
||||
size_t stridx; // position to write in string
|
||||
uint32_t bg; // queried default background or 0
|
||||
int cursor_y, cursor_x;// cursor location
|
||||
bool xtgettcap_good; // high when we've received DCS 1
|
||||
bool appsync; // application-synchronized updates advertised
|
||||
} query_state;
|
||||
|
||||
static int
|
||||
@ -879,10 +882,38 @@ pump_control_read(query_state* inits, unsigned char c){
|
||||
inits->state = STATE_DA; // could also be DECRPM/XTSMGRAPHICS
|
||||
}else if(c == '>'){
|
||||
inits->state = STATE_SDA;
|
||||
}else if(isdigit(c)){
|
||||
inits->numeric = 0;
|
||||
inits->state = STATE_CURSOR;
|
||||
}else if(c >= 0x40 && c <= 0x7E){
|
||||
inits->state = STATE_NULL;
|
||||
}
|
||||
break;
|
||||
case STATE_CURSOR:
|
||||
if(isdigit(c)){
|
||||
if(ruts_hex(&inits->numeric, c)){
|
||||
return -1;
|
||||
}
|
||||
}else if(c == ';'){
|
||||
inits->cursor_y = inits->numeric;
|
||||
inits->state = STATE_CURSOR_COL;
|
||||
inits->numeric = 0;
|
||||
}else{
|
||||
inits->state = STATE_NULL;
|
||||
}
|
||||
break;
|
||||
case STATE_CURSOR_COL:
|
||||
if(isdigit(c)){
|
||||
if(ruts_hex(&inits->numeric, c)){
|
||||
return -1;
|
||||
}
|
||||
}else if(c == 'R'){
|
||||
inits->cursor_x = inits->numeric;
|
||||
inits->state = STATE_NULL;
|
||||
}else{
|
||||
inits->state = STATE_NULL;
|
||||
}
|
||||
break;
|
||||
case STATE_DCS: // terminated by ST
|
||||
if(c == '\\'){
|
||||
//fprintf(stderr, "terminated DCS\n");
|
||||
@ -1168,7 +1199,7 @@ err:
|
||||
}
|
||||
|
||||
int ncinputlayer_init(tinfo* tcache, FILE* infp, queried_terminals_e* detected,
|
||||
unsigned* appsync){
|
||||
unsigned* appsync, int* cursor_y, int* cursor_x){
|
||||
ncinputlayer* nilayer = &tcache->input;
|
||||
setbuffer(infp, NULL, 0);
|
||||
nilayer->inputescapes = NULL;
|
||||
@ -1188,6 +1219,8 @@ int ncinputlayer_init(tinfo* tcache, FILE* infp, queried_terminals_e* detected,
|
||||
.tcache = tcache,
|
||||
.state = STATE_NULL,
|
||||
.qterm = TERMINAL_UNKNOWN,
|
||||
.cursor_x = -1,
|
||||
.cursor_y = -1,
|
||||
};
|
||||
if(control_read(csifd, &inits)){
|
||||
input_free_esctrie(&nilayer->inputescapes);
|
||||
@ -1198,6 +1231,8 @@ int ncinputlayer_init(tinfo* tcache, FILE* infp, queried_terminals_e* detected,
|
||||
tcache->termversion = inits.version;
|
||||
*detected = inits.qterm;
|
||||
*appsync = inits.appsync;
|
||||
*cursor_x = inits.cursor_x;
|
||||
*cursor_y = inits.cursor_y;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -34,7 +34,8 @@ typedef enum {
|
||||
// advertised support for application-sychronized updates, |appsync| will be
|
||||
// non-zero.
|
||||
int ncinputlayer_init(struct tinfo* tcache, FILE* infp,
|
||||
queried_terminals_e* detected, unsigned* appsync);
|
||||
queried_terminals_e* detected, unsigned* appsync,
|
||||
int* cursor_y, int* cursor_x);
|
||||
|
||||
void ncinputlayer_stop(struct ncinputlayer* nilayer);
|
||||
|
||||
|
@ -150,6 +150,46 @@ grow_esc_table(tinfo* ti, const char* tstr, escape_e esc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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")
|
||||
// ⇒ CSI ? 4 ; 6 c ("VT132 with Advanced Video and Graphics")
|
||||
// ⇒ CSI ? 6 c ("VT102")
|
||||
// ⇒ CSI ? 7 c ("VT131")
|
||||
// ⇒ CSI ? 1 2 ; Ps c ("VT125")
|
||||
// ⇒ CSI ? 6 2 ; Ps c ("VT220")
|
||||
// ⇒ CSI ? 6 3 ; Ps c ("VT320")
|
||||
// ⇒ CSI ? 6 4 ; Ps c ("VT420")
|
||||
|
||||
// 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\\"
|
||||
|
||||
// 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"
|
||||
|
||||
// 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.
|
||||
// maybe that works, maybe it doesn't. then query both color registers
|
||||
// and geometry. send XTGETTCAP for terminal name.
|
||||
static int
|
||||
send_initial_queries(int fd){
|
||||
const char queries[] = CSI_BGQ
|
||||
DSRCPR
|
||||
"\x1b[?2026$p" // query for App-sync updates
|
||||
"\x1b[=0c" // Tertiary Device Attributes
|
||||
"\x1b[>0q" // XTVERSION
|
||||
"\x1bP+q544e\x1b\\" // XTGETTCAP['TN']
|
||||
"\x1b[?1;3;256S" // try to set 256 cregs
|
||||
"\x1b[?2;1;0S" // XTSMGRAPHICS (cregs)
|
||||
"\x1b[?1;1;0S" // XTSMGRAPHICS (geometry)
|
||||
"\x1b[c"; // Device Attributes
|
||||
if(blocking_write(fd, queries, strlen(queries))){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
|
||||
size_t* tablelen, size_t* tableused){
|
||||
@ -164,14 +204,15 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// older kitty terminfo doesn't include u7. remove this when we can FIXME.
|
||||
// if we get a response to the standard cursor locator escape, we know this
|
||||
// terminal supports it, hah.
|
||||
static int
|
||||
add_u7_escape(tinfo* ti, size_t* tablelen, size_t* tableused){
|
||||
const char* u7 = get_escape(ti, ESCAPE_DSRCPR);
|
||||
if(u7){
|
||||
return 0; // already present
|
||||
}
|
||||
if(grow_esc_table(ti, "\e[6n", ESCAPE_DSRCPR, tablelen, tableused)){
|
||||
if(grow_esc_table(ti, DSRCPR, ESCAPE_DSRCPR, tablelen, tableused)){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@ -221,9 +262,6 @@ apply_term_heuristics(tinfo* ti, const char* termname, int fd,
|
||||
if(add_smulx_escapes(ti, tablelen, tableused)){
|
||||
return -1;
|
||||
}
|
||||
if(add_u7_escape(ti, tablelen, tableused)){
|
||||
return -1;
|
||||
}
|
||||
}else if(qterm == TERMINAL_ALACRITTY){
|
||||
termname = "Alacritty";
|
||||
ti->caps.quadrants = true;
|
||||
@ -286,41 +324,6 @@ apply_term_heuristics(tinfo* ti, const char* termname, int fd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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")
|
||||
// ⇒ CSI ? 4 ; 6 c ("VT132 with Advanced Video and Graphics")
|
||||
// ⇒ CSI ? 6 c ("VT102")
|
||||
// ⇒ CSI ? 7 c ("VT131")
|
||||
// ⇒ CSI ? 1 2 ; Ps c ("VT125")
|
||||
// ⇒ CSI ? 6 2 ; Ps c ("VT220")
|
||||
// ⇒ CSI ? 6 3 ; Ps c ("VT320")
|
||||
// ⇒ CSI ? 6 4 ; Ps c ("VT420")
|
||||
|
||||
// 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\\"
|
||||
|
||||
// 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.
|
||||
// maybe that works, maybe it doesn't. then query both color registers
|
||||
// and geometry. send XTGETTCAP for terminal name.
|
||||
static int
|
||||
send_initial_queries(int fd){
|
||||
const char queries[] = CSI_BGQ
|
||||
"\x1b[?2026$p" // query for App-sync updates
|
||||
"\x1b[=0c" // Tertiary Device Attributes
|
||||
"\x1b[>0q" // XTVERSION
|
||||
"\x1bP+q544e\x1b\\" // XTGETTCAP['TN']
|
||||
"\x1b[?1;3;256S" // try to set 256 cregs
|
||||
"\x1b[?2;1;0S" // XTSMGRAPHICS (cregs)
|
||||
"\x1b[?1;1;0S" // XTSMGRAPHICS (geometry)
|
||||
"\x1b[c"; // Device Attributes
|
||||
if(blocking_write(fd, queries, strlen(queries))){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// termname is just the TERM environment variable. some details are not
|
||||
// exposed via terminfo, and we must make heuristic decisions based on
|
||||
// the detected terminal type, yuck :/.
|
||||
@ -501,7 +504,9 @@ int interrogate_terminfo(tinfo* ti, int fd, const char* termname, unsigned utf8,
|
||||
}
|
||||
}
|
||||
unsigned appsync_advertised;
|
||||
if(ncinputlayer_init(ti, stdin, &qterm, &appsync_advertised)){
|
||||
int cursor_x = -1;
|
||||
int cursor_y = -1;
|
||||
if(ncinputlayer_init(ti, stdin, &qterm, &appsync_advertised, &cursor_y, &cursor_x)){
|
||||
goto err;
|
||||
}
|
||||
if(nocbreak){
|
||||
@ -512,6 +517,12 @@ int interrogate_terminfo(tinfo* ti, int fd, const char* termname, unsigned utf8,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cursor_x >= 0 && cursor_y >= 0){
|
||||
if(add_u7_escape(ti, &tablelen, &tableused)){
|
||||
return -1;
|
||||
}
|
||||
// FIXME set cursor up
|
||||
}
|
||||
if(appsync_advertised){
|
||||
if(add_appsync_escapes(ti, &tablelen, &tableused)){
|
||||
goto err;
|
||||
|
Loading…
x
Reference in New Issue
Block a user