Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
termdesc.c
Go to the documentation of this file.
1#include <fcntl.h>
2#include <unistd.h>
3#include <curses.h>
4#ifdef __linux__
5#include <sys/utsname.h>
6#endif
7#include "internal.h"
8#include "windows.h"
9#include "linux.h"
10
11// tlen -- size of escape table. tused -- used bytes in same.
12// returns -1 if the starting location is >= 65535. otherwise,
13// copies tstr into the table, and sets up 1-biased index.
14int grow_esc_table(tinfo* ti, const char* tstr, escape_e esc,
15 size_t* tlen, size_t* tused){
16 // the actual table can grow past 64KB, but we can't start there, as
17 // we only have 16-bit indices.
18 if(*tused >= 65535){
19 fprintf(stderr, "Can't add escape %d to full table\n", esc);
20 return -1;
21 }
22 if(get_escape(ti, esc)){
23 fprintf(stderr, "Already defined escape %d (%s)\n",
24 esc, get_escape(ti, esc));
25 return -1;
26 }
27 size_t slen = strlen(tstr) + 1; // count the nul term
28 if(*tlen - *tused < slen){
29 // guaranteed to give us enough space to add tstr (and then some)
30 size_t newsize = *tlen + 4020 + slen; // don't pull two pages ideally
31 char* tmp = realloc(ti->esctable, newsize);
32 if(tmp == NULL){
33 return -1;
34 }
35 ti->esctable = tmp;
36 *tlen = newsize;
37 }
38 // we now are guaranteed sufficient space to copy tstr
39 memcpy(ti->esctable + *tused, tstr, slen);
40 ti->escindices[esc] = *tused + 1; // one-bias
41 *tused += slen;
42 return 0;
43}
44
45// there does not exist any true standard terminal size. with that said, we
46// need assume *something* for the case where we're not actually attached to
47// a terminal (mainly unit tests, but also daemon environments). in preference
48// to this, we use the geometries defined by (in order of precedence):
49//
50// * TIOGWINSZ ioctl(2)
51// * LINES/COLUMNS environment variables
52// * lines/cols terminfo variables
53//
54// this function sets up ti->default_rows and ti->default_cols
55static int
56get_default_dimension(const char* envvar, const char* tinfovar, int def){
57 const char* env = getenv(envvar);
58 int num;
59 if(env){
60 num = atoi(env);
61 if(num > 0){
62 return num;
63 }
64 }
65 num = tigetnum(tinfovar);
66 if(num > 0){
67 return num;
68 }
69 return def;
70}
71
72static void
73get_default_geometry(tinfo* ti){
74 ti->default_rows = get_default_dimension("LINES", "lines", 24);
75 ti->default_cols = get_default_dimension("COLUMNS", "cols", 80);
76 loginfo("default geometry: %d row%s, %d column%s",
77 ti->default_rows, ti->default_rows != 1 ? "s" : "",
78 ti->default_cols, ti->default_cols != 1 ? "s" : "");
79 ti->dimy = ti->default_rows;
80 ti->dimx = ti->default_cols;
81}
82
83// we found Sixel support -- set up its API. invert80 refers to whether the
84// terminal implements DECSDM correctly (enabling it with \e[?80h), or inverts
85// the meaning (*disabling* it with \e[?80h) (we always want it disabled).
86static inline void
87setup_sixel_bitmaps(tinfo* ti, int fd, unsigned forcesdm, unsigned invert80){
88 if(forcesdm){
89 if(invert80){
91 }else{
93 }
94 }else{
96 }
98 ti->pixel_remove = NULL;
101 ti->pixel_draw_late = NULL;
102 ti->pixel_commit = NULL;
103 ti->pixel_move = NULL;
104 ti->pixel_scroll = NULL;
106 ti->pixel_clear_all = NULL;
109 ti->sprixel_scale_height = 6;
113 sprite_init(ti, fd);
114}
115
116// kitty 0.19.3 didn't have C=1, and thus needs sixel_maxy_pristine. it also
117// lacked animation, and must thus redraw the complete image every time it
118// changes. requires the older interface.
119static inline void
120setup_kitty_bitmaps(tinfo* ti, int fd, ncpixelimpl_e level){
124 ti->pixel_draw_late = NULL;
125 ti->pixel_refresh = NULL;
128 ti->pixel_scroll = NULL;
130 if(level == NCPIXEL_KITTY_STATIC){
134 ti->sixel_maxy_pristine = INT_MAX;
137 }else{
138 if(level == NCPIXEL_KITTY_ANIMATED){
141 ti->sixel_maxy_pristine = 0;
144 }else{
147 ti->sixel_maxy_pristine = 0;
150 }
151 }
152 sprite_init(ti, fd);
153}
154
155#ifdef __linux__
156static inline void
157setup_fbcon_bitmaps(tinfo* ti, int fd){
159 ti->pixel_remove = NULL;
160 ti->pixel_draw = NULL;
162 ti->pixel_commit = NULL;
163 ti->pixel_refresh = NULL;
164 ti->pixel_move = NULL;
166 ti->pixel_clear_all = NULL;
172 sprite_init(ti, fd);
173}
174#endif
175
176static bool
177query_rgb(void){
178 bool rgb = (tigetflag("RGB") > 0 || tigetflag("Tc") > 0);
179 if(!rgb){
180 // RGB terminfo capability being a new thing (as of ncurses 6.1), it's not
181 // commonly found in terminal entries today. COLORTERM, however, is a
182 // de-facto (if imperfect/kludgy) standard way of indicating TrueColor
183 // support for a terminal. The variable takes one of two case-sensitive
184 // values:
185 //
186 // truecolor
187 // 24bit
188 //
189 // https://gist.github.com/XVilka/8346728#true-color-detection gives some
190 // more information about the topic.
191 const char* cterm = getenv("COLORTERM");
192 rgb = cterm && (strcmp(cterm, "truecolor") == 0 || strcmp(cterm, "24bit") == 0);
193 }
194 return rgb;
195}
196
198 stop_inputlayer(ti);
199 loginfo("brought down input layer");
200 if(ti->pixel_cleanup){
201 ti->pixel_cleanup(ti);
202 }
203 free(ti->termversion);
204 free(ti->esctable);
205#ifdef __linux__
206 if(ti->linux_fb_fd >= 0){
207 close(ti->linux_fb_fd);
208 }
209 free(ti->linux_fb_dev);
210 if(ti->linux_fbuffer != MAP_FAILED){
211 munmap(ti->linux_fbuffer, ti->linux_fb_len);
212 }
213#endif
214 free(ti->tpreserved);
215 loginfo("destroyed terminfo cache");
216}
217
218// compare one terminal version against another. numerics, separated by
219// periods, and comparison ends otherwise (so "20.0 alpha" doesn't compare
220// as greater than "20.0", mainly). returns -1 if v1 < v2 (or v1 is NULL),
221// 0 if v1 == v2, or 1 if v1 > v2.
222static int
223compare_versions(const char* restrict v1, const char* restrict v2){
224 if(v1 == NULL){
225 return -1;
226 }
227 const char* v1e = v1;
228 const char* v2e = v2;
229 while(*v1 && *v2){
230 long v1v = strtol(v1, (char **)&v1e, 10);
231 long v2v = strtol(v2, (char **)&v2e, 10);
232 if(v1e == v1 && v2e == v2){ // both are done
233 return 0;
234 }else if(v1e == v1){ // first is done
235 return -1;
236 }else if(v2e == v2){ // second is done
237 return 1;
238 }
239 if(v1v > v2v){
240 return 1;
241 }else if(v2v > v1v){
242 return -1;
243 }
244 if(*v1e != '.' && *v2e != '.'){
245 break;
246 }else if(*v1e != '.' || *v2e != '.'){
247 if(*v1e == '.'){
248 return 1;
249 }else{
250 return -1;
251 }
252 }
253 v1 = v1e + 1;
254 v2 = v2e + 1;
255 }
256 if(*v1e == *v2e){
257 return 0;
258 }
259 // can only get out here if at least one was not a period
260 if(*v1e == '.'){
261 return 1;
262 }
263 if(*v2e == '.'){
264 return -1;
265 }
266 if(!*v1e){
267 return -1;
268 }
269 if(!*v2e){
270 return 1;
271 }
272 return 0;
273}
274
275static inline int
276terminfostr(char** gseq, const char* name){
277 *gseq = tigetstr(name);
278 if(*gseq == NULL || *gseq == (char*)-1){
279 *gseq = NULL;
280 return -1;
281 }
282 // terminfo syntax allows a number N of milliseconds worth of pause to be
283 // specified using $<N> syntax. this is then honored by tputs(). but we don't
284 // use tputs(), instead preferring the much faster stdio+tiparm() (at the
285 // expense of terminals which do require these delays). to avoid dumping
286 // "$<N>" sequences all over stdio, we chop them out. real text can follow
287 // them, so we continue on, copying back once out of the delay.
288 char* wnext = NULL; // NULL until we hit a delay, then place to write
289 bool indelay = false; // true iff we're in a delay section
290 // we consider it a delay as soon as we see '$', and the delay ends at '>'
291 for(char* cur = *gseq ; *cur ; ++cur){
292 if(!indelay){
293 // if we're not in a delay section, make sure we're not starting one,
294 // and otherwise copy the current character back (if necessary).
295 if(*cur == '$'){
296 wnext = cur;
297 indelay = true;
298 }else{
299 if(wnext){
300 *wnext++ = *cur;
301 }
302 }
303 }else{
304 // we are in a delay section. make sure we're not ending one.
305 if(*cur == '>'){
306 indelay = false;
307 }
308 }
309 }
310 if(wnext){
311 *wnext = '\0';
312 }
313 return 0;
314}
315
316static inline int
317init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
318 size_t* tablelen, size_t* tableused){
319 char* tstr;
320 if(ti->escindices[idx]){
321 return 0;
322 }
323 if(terminfostr(&tstr, name) == 0){
324 if(grow_esc_table(ti, tstr, idx, tablelen, tableused)){
325 return -1;
326 }
327 }else{
328 ti->escindices[idx] = 0;
329 }
330 return 0;
331}
332
333// Tertiary Device Attributes, necessary to identify VTE.
334// https://vt100.net/docs/vt510-rm/DA3.html
335// Replies with DCS ! | ... ST
336#define TRIDEVATTR "\x1b[=c"
337
338// Primary Device Attributes, necessary to elicit a response from terminals
339// which don't respond to other queries. All known terminals respond to DA1.
340// https://vt100.net/docs/vt510-rm/DA1.html
341// Device Attributes; replies with (depending on decTerminalID resource):
342// ⇒ CSI ? 1 ; 2 c ("VT100 with Advanced Video Option")
343// ⇒ CSI ? 1 ; 0 c ("VT101 with No Options")
344// ⇒ CSI ? 4 ; 6 c ("VT132 with Advanced Video and Graphics")
345// ⇒ CSI ? 6 c ("VT102")
346// ⇒ CSI ? 7 c ("VT131")
347// ⇒ CSI ? 1 2 ; Ps c ("VT125")
348// ⇒ CSI ? 6 2 ; Ps c ("VT220")
349// ⇒ CSI ? 6 3 ; Ps c ("VT320")
350// ⇒ CSI ? 6 4 ; Ps c ("VT420")
351#define PRIDEVATTR "\x1b[c"
352
353// XTVERSION. Replies with DCS > | ... ST
354#define XTVERSION "\x1b[>0q"
355
356// ideally we'd abandon terminfo entirely (terminfo is great; TERM sucks), and
357// get all properties through terminal queries. we don't yet, but grab a few
358// of importance that we know to oftentimes be incorrect:
359// * TN (544e): terminal name; a poor man's XTVERSION
360// * RGB (524742): 24-bit color is supported via setaf/setab
361// * hpa (687061): broken in Kitty FreeBSD terminfo (#2541)
362// XTGETTCAP['TN', 'RGB', 'hpa']
363// (Terminal Name, RGB, Horizontal Position Absolute)
364#define XTGETTCAP "\x1bP+q544e;524742;687061\x1b\\"
365
366// Secondary Device Attributes, necessary to get Alacritty's version. Since
367// this doesn't uniquely identify a terminal, we ask it last, so that if any
368// queries which *do* unambiguously identify a terminal have succeeded, this
369// needn't be paid attention to.
370// https://vt100.net/docs/vtk510-rm/DA2.html
371// (note that tmux uses 84 rather than common 60/61)
372// Replies with CSI > \d \d ; Pv ; [01] c
373#define SECDEVATTR "\x1b[>c"
374
375// query for kitty graphics. if they are supported, we'll get a response to
376// this using the kitty response syntax. otherwise, we'll get nothing. we
377// send this with the other identification queries, since APCs tend not to
378// be consumed by certain terminal emulators (looking at you, Linux console)
379// which can be identified directly, sans queries.
380// we do not send this query on Windows because it is bled through ConHost,
381// and echoed onto the standard output.
382#ifndef __MINGW32__
383#define KITTYQUERY "\x1b_Gi=1,a=q;\x1b\\"
384#else
385#define KITTYQUERY
386#endif
387
388// request kitty keyboard protocol features 1, 2, 8 and 16, first pushing current.
389// see https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
390#define KKBDSUPPORT "\x1b[=27u"
391
392// the kitty keyboard protocol allows unambiguous, complete identification of
393// input events. this queries for the level of support. we want to do this
394// because the "keyboard pop" control code is mishandled by kitty < 0.20.0.
395#define KKBDQUERY "\x1b[?u"
396
397// set modifyFunctionKeys (2) if supported, allowing us to disambiguate
398// function keys when used with modifiers. set modifyOtherKeys (4) if
399// supported. these ought follow keyboard push and precede kitty keyboard.
400#define XTMODKEYS "\x1b[>2;1m\x1b[>4;2m"
401
402// these queries can hopefully uniquely and unquestionably identify the
403// terminal to which we are talking. if we already know what we're talking
404// to, there's no point in sending them.
405#define IDQUERIES TRIDEVATTR \
406 XTVERSION \
407 XTGETTCAP \
408 SECDEVATTR
409
410// query background, replies in X color https://www.x.org/releases/X11R7.7/doc/man/man7/X.7.xhtml#heading11
411// GNU screen passes this on to the underlying terminal rather than answering itself,
412// unlike most other queries, so send this first since it will take longer to be
413// answered. note the "\x1b]"; this is an Operating System Command, not CSI.
414#define DEFBGQ "\x1b]11;?\e\\"
415#define DEFFGQ "\x1b]10;?\e\\"
416
417// FIXME ought be using the u7 terminfo string here, if it exists. the great
418// thing is, if we get a response to this, we know we can use it for u7!
419// we send this first because terminals which don't consume the entire escape
420// sequences following will bleed the excess into the terminal, and we want
421// to blow any such output away (or at least return to the cell where such
422// output started).
423#define DSRCPR "\x1b[6n"
424
425// check for Synchronized Update Mode support. the p is necessary, but at
426// least Konsole and Terminal.app fail to consume it =[.
427#define SUMQUERY "\x1b[?2026$p"
428
429// check for mouse mode 1016, pixel-based reports
430#define PIXELMOUSEQUERY "\x1b[?1016$p"
431
432// XTSMGRAPHICS query for the number of color registers.
433#define CREGSXTSM "\x1b[?2;1;0S"
434
435// XTSMGRAPHICS query for the maximum supported geometry.
436#define GEOMXTSM "\x1b[?1;1;0S"
437
438// non-standard CSI for total pixel geometry
439#define GEOMPIXEL "\x1b[14t"
440
441// request the cell geometry of the textual area
442#define GEOMCELL "\x1b[18t"
443
444// palette queries are logically part of DIRECTIVES, but we generate
445// those on the fly (they would otherwise be quite a lot of rodata).
446#define DIRECTIVES DEFFGQ \
447 DEFBGQ \
448 KKBDQUERY \
449 SUMQUERY \
450 PIXELMOUSEQUERY \
451 "\x1b[?1;3;256S" /* try to set 256 cregs */ \
452 "\x1b[?1;3;1024S" /* try to set 1024 cregs */ \
453 KITTYQUERY \
454 CREGSXTSM \
455 GEOMXTSM \
456 GEOMPIXEL \
457 GEOMCELL \
458 PRIDEVATTR
459
460// kitty keyboard push, used at start
461#define KKEYBOARD_PUSH "\x1b[>u"
462
463// written whenever we switch between standard and alternate screen, or upon
464// startup (that's an entry into a screen! presumably the standard one).
465#define KKBDENTER KKEYBOARD_PUSH KKBDSUPPORT
466
467// enter the alternate screen (smcup). we could technically get this from
468// terminfo, but everyone who supports it supports it the same way, and we
469// need to send it before our other directives if we're going to use it.
470// we warn later in setup if what we get from terminfo doesn't match what
471// we sent here.
472#define SMCUP DECSET(SET_SMCUP)
473#define RMCUP DECRST(SET_SMCUP)
474
475static ssize_t
476send_initial_directives(queried_terminals_e qterm, int fd){
477 int total = 0;
478// 4096 is more than sufficient for up through 256 OSC queries
479#define PQUERYBUFLEN 4096
480 if(qterm != TERMINAL_LINUX){
481 // FIXME linux kernel does not yet support OSC4, and bleeds it. don't send
482 // palette queries on linux VT.
483 char* pqueries = malloc(PQUERYBUFLEN);
484 if(pqueries == NULL){
485 return -1;
486 }
487 // bunch the queries up according to known palette sizes, so that we don't
488 // knock out batched OSCs with error responses.
489 const int qsets[] = { 0, 8, 16, 88, 256 };
490 for(size_t q = 1 ; q < sizeof(qsets) / sizeof(*qsets) ; ++q){
491 int len = 0;
492 for(int i = qsets[q - 1] ; i < qsets[q] ; ++i){
493 len += sprintf(pqueries + len, "\x1b]4;%d;?\e\\", i);
495 }
496 if(blocking_write(fd, pqueries, len)){
497 return -1;
498 }
499 total += len;
500 }
501 free(pqueries);
502 }
503#undef PQUERYBUFLEN
504 if(blocking_write(fd, DIRECTIVES, strlen(DIRECTIVES))){
505 return -1;
506 }
507 total += strlen(DIRECTIVES);
508 return total;
509}
510
511// we send an XTSMGRAPHICS to set up 256 (or ideally 1024) color registers.
512// maybe that works, maybe it doesn't. then query both color registers and
513// geometry. send XTGETTCAP for terminal name. if 'minimal' is set, don't send
514// any identification queries (we've already identified the terminal). write
515// DSRCPR as early as possible, so that it precedes any query material that's
516// bled onto stdin and echoed. if 'noaltscreen' is set, do not send an smcup.
517// if 'draininput' is set, do not send any keyboard modifiers.
518// precondition: ti->ttyfd is a valid fd (we're connected to a terminal)
519static int
520send_initial_queries(tinfo* ti, unsigned minimal, unsigned noaltscreen,
521 unsigned draininput){
522 int fd = ti->ttyfd;
523 size_t total = 0;
524 // everything sends DSRCPR, and everything sends DIRECTIVES afterwards.
525 // we send KKBDENTER immediately before DIRECTIVES unless input is being
526 // drained. we send IDQUERIES unless minimal is set. we send SMCUP (as
527 // the first thing) unless noaltscreen is set.
528 if(!noaltscreen){
529 if(blocking_write(fd, SMCUP, strlen(SMCUP))){
530 return -1;
531 }
532 total += strlen(SMCUP);
533 }
534 if(blocking_write(fd, DSRCPR, strlen(DSRCPR))){
535 return -1;
536 }
537 total += strlen(DSRCPR);
538 if(!draininput){
539 if(blocking_write(fd, KKBDENTER, strlen(KKBDENTER))){
540 return -1;
541 }
542 total += strlen(KKBDENTER);
543 }
544 if(!minimal){
545 if(blocking_write(fd, IDQUERIES, strlen(IDQUERIES))){
546 return -1;
547 }
548 total += strlen(IDQUERIES);
549 }
550 ssize_t directiveb = send_initial_directives(ti->qterm, fd);
551 if(directiveb < 0){
552 return -1;
553 }
554 total += directiveb;
555 loginfo("sent %" PRIuPTR "B", total);
556 return 0;
557}
558
559int enter_alternate_screen(int fd, FILE* ttyfp, tinfo* ti, unsigned drain){
560 if(ti->in_alt_screen){
561 return 0;
562 }
563 const char* popcolors = get_escape(ti, ESCAPE_RESTORECOLORS);
564 if(popcolors){
565 if(term_emit(popcolors, ttyfp, true)){
566 return -1;
567 }
568 }
569 const char* smcup = get_escape(ti, ESCAPE_SMCUP);
570 if(smcup == NULL){
571 logerror("alternate screen is unavailable");
572 return -1;
573 }
574 if(!drain){
575 if(ti->kbdlevel){
576 if(tty_emit(KKEYBOARD_POP, fd)){
577 return -1;
578 }
579 }else{
580 if(tty_emit(XTMODKEYSUNDO, fd)){
581 return -1;
582 }
583 }
584 }
585 if(tty_emit(smcup, fd) < 0){
586 return -1;
587 }
588 if(!drain){
589 if(ti->kbdlevel){
590 if(tty_emit(KKBDENTER, fd)){
591 return -1;
592 }
593 }else{
594 if(tty_emit(XTMODKEYS, fd)){
595 return -1;
596 }
597 }
598 }
599 const char* pushcolors = get_escape(ti, ESCAPE_SAVECOLORS);
600 if(pushcolors){
601 if(term_emit(pushcolors, ttyfp, true)){
602 return -1;
603 }
604 }
605 ti->in_alt_screen = true;
606 return 0;
607}
608
609// we need to send the palette push/pop to the bulk out (as that's where the
610// palette reprogramming happens), but rmcup+keyboard go to ttyfd.
611int leave_alternate_screen(int fd, FILE* fp, tinfo* ti, unsigned drain){
612 if(!ti->in_alt_screen){
613 return 0;
614 }
615 const char* rmcup = get_escape(ti, ESCAPE_RMCUP);
616 if(rmcup == NULL){
617 logerror("can't leave alternate screen");
618 return -1;
619 }
620 if(!drain){
621 if(ti->kbdlevel){
622 if(tty_emit(KKEYBOARD_POP, fd)){
623 return -1;
624 }
625 }else{
626 if(tty_emit(XTMODKEYSUNDO, fd)){
627 return -1;
628 }
629 }
630 }
631 const char* popcolors = get_escape(ti, ESCAPE_RESTORECOLORS);
632 if(popcolors){
633 if(term_emit(popcolors, fp, true)){
634 return -1;
635 }
636 }
637 if(tty_emit(rmcup, fd)){
638 return -1;
639 }
640 if(!drain){
641 if(ti->kbdlevel){
642 if(tty_emit(KKBDENTER, fd)){
643 return -1;
644 }
645 }else{
646 if(tty_emit(XTMODKEYS, fd)){
647 return -1;
648 }
649 }
650 }
651 const char* pushcolors = get_escape(ti, ESCAPE_SAVECOLORS);
652 if(pushcolors){
653 if(term_emit(popcolors, fp, true)){
654 return -1;
655 }
656 }
657 ti->in_alt_screen = false;
658 return 0;
659}
660
661// if we get a response to the standard cursor locator escape, we know this
662// terminal supports it, hah.
663static int
664add_u7_escape(tinfo* ti, size_t* tablelen, size_t* tableused){
665 const char* u7 = get_escape(ti, ESCAPE_U7);
666 if(u7){
667 return 0; // already present
668 }
669 if(grow_esc_table(ti, DSRCPR, ESCAPE_U7, tablelen, tableused)){
670 return -1;
671 }
672 return 0;
673}
674
675static int
676add_smulx_escapes(tinfo* ti, size_t* tablelen, size_t* tableused){
677 if(get_escape(ti, ESCAPE_SMULX)){
678 return 0;
679 }
680 if(grow_esc_table(ti, "\x1b[4:3m", ESCAPE_SMULX, tablelen, tableused) ||
681 grow_esc_table(ti, "\x1b[4:0m", ESCAPE_SMULNOX, tablelen, tableused)){
682 return -1;
683 }
684 return 0;
685}
686
687static inline void
688kill_escape(tinfo* ti, escape_e e){
689 ti->escindices[e] = 0;
690}
691
692static void
693kill_appsync_escapes(tinfo* ti){
694 kill_escape(ti, ESCAPE_BSUM);
695 kill_escape(ti, ESCAPE_ESUM);
696}
697
698static int
699add_appsync_escapes_sm(tinfo* ti, size_t* tablelen, size_t* tableused){
700 if(get_escape(ti, ESCAPE_BSUM)){
701 return 0;
702 }
703 if(grow_esc_table(ti, "\x1b[?2026h", ESCAPE_BSUM, tablelen, tableused) ||
704 grow_esc_table(ti, "\x1b[?2026l", ESCAPE_ESUM, tablelen, tableused)){
705 return -1;
706 }
707 return 0;
708}
709
710static int
711add_appsync_escapes_dcs(tinfo* ti, size_t* tablelen, size_t* tableused){
712 if(get_escape(ti, ESCAPE_BSUM)){
713 return 0;
714 }
715 if(grow_esc_table(ti, "\x1bP=1s\x1b\\", ESCAPE_BSUM, tablelen, tableused) ||
716 grow_esc_table(ti, "\x1bP=2s\x1b\\", ESCAPE_ESUM, tablelen, tableused)){
717 return -1;
718 }
719 return 0;
720}
721
722static int
723add_pushcolors_escapes(tinfo* ti, size_t* tablelen, size_t* tableused){
724 if(get_escape(ti, ESCAPE_SAVECOLORS)){
725 return 0;
726 }
727 if(grow_esc_table(ti, "\x1b[#P", ESCAPE_SAVECOLORS, tablelen, tableused) ||
728 grow_esc_table(ti, "\x1b[#Q", ESCAPE_RESTORECOLORS, tablelen, tableused)){
729 return -1;
730 }
731 return 0;
732}
733
734static const char*
735apply_kitty_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused){
736 // see https://sw.kovidgoyal.net/kitty/protocol-extensions.html
737 ti->bg_collides_default |= 0x1000000;
738 ti->caps.sextants = true; // work since bugfix in 0.19.3
739 ti->caps.quadrants = true;
740 ti->caps.rgb = true;
741 if(add_smulx_escapes(ti, tablelen, tableused)){
742 return NULL;
743 }
744 /*if(compare_versions(ti->termversion, "0.22.1") >= 0){
745 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_SELFREF);
746 }else*/ if(compare_versions(ti->termversion, "0.20.0") >= 0){
747 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_ANIMATED);
748 // XTPOPCOLORS didn't reliably work until a bugfix late in 0.23.1 (see
749 // https://github.com/kovidgoyal/kitty/issues/4351), so reprogram the
750 // font directly until we exceed that version.
751 if(compare_versions(ti->termversion, "0.23.1") > 0){
752 if(add_pushcolors_escapes(ti, tablelen, tableused)){
753 return NULL;
754 }
755 }
756 }else{
757 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_STATIC);
758 }
759 // kitty SUM doesn't want long sequences, which is exactly where we use
760 // it. remove support (we pick it up from queries).
761 kill_appsync_escapes(ti);
762 ti->gratuitous_hpa = true;
763 return "Kitty";
764}
765
766static const char*
767apply_alacritty_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
768 bool* forcesdm, bool* invertsixel){
769 ti->caps.quadrants = true;
770 // ti->caps.sextants = true; // alacritty https://github.com/alacritty/alacritty/issues/4409
771 ti->caps.rgb = true;
772 // Alacritty implements DCS ASU, but no detection for it
773 if(add_appsync_escapes_dcs(ti, tablelen, tableused)){
774 return NULL;
775 }
776 *forcesdm = true;
777 if(compare_versions(ti->termversion, "0.15.1") < 0){
778 *invertsixel = true;
779 }
780 return "Alacritty";
781}
782
783static const char*
784apply_vte_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused){
785 ti->caps.quadrants = true;
786 ti->caps.sextants = true; // VTE has long enjoyed good sextant support
787 if(add_smulx_escapes(ti, tablelen, tableused)){
788 return NULL;
789 }
790 // VTE understands DSC ACU, but doesn't do anything with it; don't use it
791 return "VTE";
792}
793
794static const char*
795apply_foot_heuristics(tinfo* ti, bool* forcesdm, bool* invertsixel){
796 ti->caps.sextants = true;
797 ti->caps.quadrants = true;
798 ti->caps.rgb = true;
799 *forcesdm = true;
800 if(compare_versions(ti->termversion, "1.8.2") < 0){
801 *invertsixel = true;
802 }
803 return "foot";
804}
805
806static const char*
807apply_gnuscreen_heuristics(tinfo* ti){
808 if(compare_versions(ti->termversion, "5.0") < 0){
809 ti->caps.rgb = false;
810 }
811 return "GNU screen";
812}
813
814static const char*
815apply_mlterm_heuristics(tinfo* ti){
816 ti->caps.quadrants = true; // good caps.quadrants, no caps.sextants as of 3.9.0
817 return "MLterm";
818}
819
820static const char*
821apply_wezterm_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused){
822 ti->caps.rgb = true;
823 ti->caps.quadrants = true;
824 if(ti->termversion && strcmp(ti->termversion, "20210610") >= 0){
825 ti->caps.sextants = true; // good caps.sextants as of 2021-06-10
826 if(add_smulx_escapes(ti, tablelen, tableused)){
827 return NULL;
828 }
829 }
830 return "WezTerm";
831}
832
833static const char*
834apply_xterm_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
835 bool* forcesdm, bool* invertsixel){
836 *forcesdm = true;
837 if(compare_versions(ti->termversion, "369") < 0){
838 *invertsixel = true; // xterm 369 inverted DECSDM
839 }
840 // xterm 357 added color palette escapes XT{PUSH,POP,REPORT}COLORS
841 if(compare_versions(ti->termversion, "357") >= 0){
842 if(add_pushcolors_escapes(ti, tablelen, tableused)){
843 return NULL;
844 }
845 }
846 return "XTerm";
847}
848
849static const char*
850apply_mintty_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
851 bool* forcesdm, bool* invertsixel){
852 if(add_smulx_escapes(ti, tablelen, tableused)){
853 return NULL;
854 }
855 *forcesdm = true;
856 if(compare_versions(ti->termversion, "3.5.2") < 0){
857 *invertsixel = true;
858 }
859 ti->bce = true;
860 return "MinTTY";
861}
862
863static const char*
864apply_msterminal_heuristics(tinfo* ti){
865 ti->caps.rgb = true;
866 ti->caps.quadrants = true;
867 return "Windows ConHost";
868}
869
870static const char*
871apply_contour_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
872 bool* forcesdm, bool* invertsixel){
873 if(add_smulx_escapes(ti, tablelen, tableused)){
874 return NULL;
875 }
876 if(add_pushcolors_escapes(ti, tablelen, tableused)){
877 return NULL;
878 }
879 ti->caps.quadrants = true;
880 ti->caps.sextants = true;
881 ti->caps.rgb = true;
882 *forcesdm = true;
883 *invertsixel = false;
884 return "Contour";
885}
886
887static const char*
888apply_iterm_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused){
889 // iTerm implements DCS ASU, but has no detection for it
890 if(add_appsync_escapes_dcs(ti, tablelen, tableused)){
891 return NULL;
892 }
893 ti->caps.quadrants = true;
894 ti->caps.rgb = true;
895 return "iTerm2";
896}
897
898static const char*
899apply_rxvt_heuristics(tinfo* ti){
900 ti->caps.braille = false;
901 ti->caps.quadrants = true;
902 return "RXVT";
903}
904
905static const char*
906apply_terminology_heuristics(tinfo* ti){
907 // no RGB as of at least 1.9.0
908 ti->caps.quadrants = true;
909 return "Terminology";
910}
911
912static const char*
913apply_konsole_heuristics(tinfo* ti){
914 ti->caps.rgb = true;
915 ti->caps.quadrants = true;
916 return "Konsole";
917}
918
919static const char*
920apply_linux_heuristics(tinfo* ti, unsigned nonewfonts){
921 const char* tname = NULL;
922#ifdef __linux__
923 struct utsname un;
924 if(uname(&un) == 0){
925 ti->termversion = strdup(un.release);
926 }
927 if(is_linux_framebuffer(ti)){
928 tname = "FBcon";
929 setup_fbcon_bitmaps(ti, ti->linux_fb_fd);
930 }else{
931 tname = "VT";
932 }
933 ti->caps.halfblocks = false;
934 ti->caps.braille = false; // no caps.braille, no caps.sextants in linux console
935 if(ti->ttyfd >= 0){
936 reprogram_console_font(ti, nonewfonts, &ti->caps.halfblocks,
937 &ti->caps.quadrants);
938 }
939 // assume no useful unicode drawing unless we're positively sure
940#else
941 (void)ti;
942 (void)nonewfonts;
943#endif
944 return tname;
945}
946
947// qui si convien lasciare ogne sospetto; ogne viltà convien che qui sia morta.
948// in a more perfect world, this function would not exist, but this is a
949// regrettably imperfect world, and thus all manner of things are not maintained
950// in terminfo, and old terminfos abound, and users don't understand terminfo,
951// so we override and/or supply various properties based on terminal
952// identification performed earlier. we still get most things from terminfo,
953// though, so it's something of a worst-of-all-worlds deal where TERM still
954// needs be correct, even though we identify the terminal. le sigh.
955static int
956apply_term_heuristics(tinfo* ti, const char* tname, queried_terminals_e qterm,
957 size_t* tablelen, size_t* tableused,
958 bool* forcesdm, bool* invertsixel,
959 unsigned nonewfonts){
960#ifdef __MINGW32__
961 if(qterm == TERMINAL_UNKNOWN){
962 qterm = TERMINAL_MSTERMINAL;
963 }
964#endif
965 if(!tname){
966 // setupterm interprets a missing/empty TERM variable as the special value “unknown”.
967 tname = ti->termname ? ti->termname : "unknown";
968 }
969 // st had neither caps.sextants nor caps.quadrants last i checked (0.8.4)
970 ti->caps.braille = true; // most everyone has working caps.braille, even from fonts
971 ti->caps.halfblocks = true; // most everyone has working halfblocks
972 const char* newname = NULL;
973 switch(qterm){
974 case TERMINAL_KITTY:
975 newname = apply_kitty_heuristics(ti, tablelen, tableused);
976 break;
978 newname = apply_alacritty_heuristics(ti, tablelen, tableused,
979 forcesdm, invertsixel);
980 break;
981 case TERMINAL_VTE:
982 newname = apply_vte_heuristics(ti, tablelen, tableused);
983 break;
984 case TERMINAL_FOOT:
985 newname = apply_foot_heuristics(ti, forcesdm, invertsixel);
986 break;
987 case TERMINAL_TMUX:
988 newname = "tmux"; // FIXME what, oh what to do with tmux?
989 break;
991 newname = apply_gnuscreen_heuristics(ti);
992 break;
993 case TERMINAL_MLTERM:
994 newname = apply_mlterm_heuristics(ti);
995 break;
996 case TERMINAL_WEZTERM:
997 newname = apply_wezterm_heuristics(ti, tablelen, tableused);
998 break;
999 case TERMINAL_XTERM:
1000 newname = apply_xterm_heuristics(ti, tablelen, tableused,
1001 forcesdm, invertsixel);
1002 break;
1003 case TERMINAL_MINTTY:
1004 newname = apply_mintty_heuristics(ti, tablelen, tableused,
1005 forcesdm, invertsixel);
1006 break;
1008 newname = apply_msterminal_heuristics(ti);
1009 break;
1010 case TERMINAL_CONTOUR:
1011 newname = apply_contour_heuristics(ti, tablelen, tableused,
1012 forcesdm, invertsixel);
1013 break;
1014 case TERMINAL_ITERM:
1015 newname = apply_iterm_heuristics(ti, tablelen, tableused);
1016 break;
1017 case TERMINAL_RXVT:
1018 newname = apply_rxvt_heuristics(ti);
1019 break;
1020 case TERMINAL_APPLE:
1021 newname = "Terminal.app"; // no quadrants, no sextants, no rgb, but it does have braille
1022 break;
1023 case TERMINAL_LINUX:
1024 newname = apply_linux_heuristics(ti, nonewfonts);
1025 break;
1027 newname = apply_terminology_heuristics(ti);
1028 break;
1029 case TERMINAL_KONSOLE:
1030 newname = apply_konsole_heuristics(ti);
1031 break;
1032 default:
1033 newname = tname;
1034 break;
1035 }
1036 if(newname == NULL){
1037 logerror("no name provided for termtype %d", qterm);
1038 return -1;
1039 }
1040 tname = newname;
1041 // run a wcwidth(⣿) to guarantee libc Unicode 3 support, independent of term
1042 if(wcwidth(L'⣿') < 0){
1043 ti->caps.braille = false;
1044 }
1045 // run a wcwidth(🬸) to guarantee libc Unicode 13 support, independent of term
1046 if(wcwidth(L'🬸') < 0){
1047 ti->caps.sextants = false;
1048 }
1049 // run a wcwidth(𜴀) to guarantee libc Unicode 16 support, independent of term
1050 if(wcwidth(L'𜴀') < 0){
1051 ti->caps.octants = false;
1052 }
1053 ti->termname = tname;
1054 return 0;
1055}
1056
1057// some terminals cannot combine certain styles with colors, as expressed in
1058// the "ncv" terminfo capability (using ncurses-style constants). don't
1059// advertise support for the style in that case. otherwise, if the style is
1060// supported, OR it into supported_styles (using Notcurses-style constants).
1061static void
1062build_supported_styles(tinfo* ti){
1063 const struct style {
1064 unsigned s; // NCSTYLE_* value
1065 int esc; // ESCAPE_* value for enable
1066 const char* tinfo; // terminfo capability for conditional permit
1067 unsigned ncvbit; // bit in "ncv" mask for unconditional deny
1068 } styles[] = {
1069 { NCSTYLE_BOLD, ESCAPE_BOLD, "bold", A_BOLD },
1070 { NCSTYLE_UNDERLINE, ESCAPE_SMUL, "smul", A_UNDERLINE },
1071 { NCSTYLE_ITALIC, ESCAPE_SITM, "sitm", A_ITALIC },
1072 { NCSTYLE_STRUCK, ESCAPE_SMXX, "smxx", 0 },
1073 { NCSTYLE_UNDERCURL, ESCAPE_SMULX, "Smulx", 0 },
1074 { 0, 0, NULL, 0 }
1075 };
1076 int nocolor_stylemask = tigetnum("ncv");
1077 for(typeof(*styles)* s = styles ; s->s ; ++s){
1078 if(get_escape(ti, s->esc)){
1079 if(nocolor_stylemask > 0){
1080 if(nocolor_stylemask & s->ncvbit){
1081 ti->escindices[s->esc] = 0;
1082 continue;
1083 }
1084 }
1085 ti->supported_styles |= s->s;
1086 }
1087 }
1088}
1089
1090#ifdef __APPLE__
1091// Terminal.App is a wretched piece of shit that can't handle even the most
1092// basic of queries, instead bleeding them through to stdout like a great
1093// wounded hippopotamus. it does export "TERM_PROGRAM=Apple_Terminal", becuase
1094// it is a committee on sewage and drainage where all the members have
1095// tourette's. on mac os, if TERM_PROGRAM=Apple_Terminal, accept this hideous
1096// existence, circumvent all queries, and may god have mercy on our souls.
1097// of course that means if a terminal launched from Terminal.App doesn't clear
1098// or reset this environment variable, they're cursed to live as Terminal.App.
1099// i'm likewise unsure what we're supposed to do should you ssh anywhere =[.
1101macos_early_matches(void){
1102 const char* tp = getenv("TERM_PROGRAM");
1103 if(tp == NULL){
1104 return TERMINAL_UNKNOWN;
1105 }
1106 if(strcmp(tp, "Apple_Terminal")){
1107 return TERMINAL_UNKNOWN;
1108 }
1109 return TERMINAL_APPLE;
1110}
1111#endif
1112
1113#ifndef __APPLE__
1114#ifndef __MINGW32__
1115// rxvt has a deeply fucked up palette code implementation. its responses are
1116// terminated with a bare ESC instead of BEL or ST, impossible to encode in
1117// our automaton alongside the proper flow. its "oc" doesn't reset the palette,
1118// meaning we must preserve and reload it ourselves. there's no way to identify
1119// rxvt via query, so if we get it in TERM, set up our automaton for its fubar
1120// replies, and don't bother sending any identification requests.
1122unix_early_matches(const char* term){
1123 if(term == NULL){
1124 return TERMINAL_UNKNOWN;
1125 }
1126 // urxvt likewise declares TERM=rxvt-whatever
1127 if(strncmp(term, "rxvt", 4) == 0){
1128 return TERMINAL_RXVT;
1129 }
1130 return TERMINAL_UNKNOWN;
1131}
1132#endif
1133#endif
1134
1135static int
1136do_terminfo_lookups(tinfo *ti, size_t* tablelen, size_t* tableused){
1137 // don't list any here for which we also send XTGETTCAP sequences
1138 const struct strtdesc {
1139 escape_e esc;
1140 const char* tinfo;
1141 } strtdescs[] = {
1142 { ESCAPE_CUP, "cup", },
1143 { ESCAPE_VPA, "vpa", },
1144 // Not all terminals support setting the fore/background independently
1145 { ESCAPE_SETAF, "setaf", },
1146 { ESCAPE_SETAB, "setab", },
1147 { ESCAPE_OP, "op", },
1148 { ESCAPE_CNORM, "cnorm", },
1149 { ESCAPE_CIVIS, "civis", },
1150 { ESCAPE_SGR0, "sgr0", },
1151 { ESCAPE_SITM, "sitm", },
1152 { ESCAPE_RITM, "ritm", },
1153 { ESCAPE_BOLD, "bold", },
1154 { ESCAPE_CUD, "cud", },
1155 { ESCAPE_CUU, "cuu", },
1156 { ESCAPE_CUF, "cuf", },
1157 { ESCAPE_CUB, "cub", },
1158 { ESCAPE_U7, "u7", },
1159 { ESCAPE_SMKX, "smkx", },
1160 { ESCAPE_SMXX, "smxx", },
1161 { ESCAPE_EL, "el", },
1162 { ESCAPE_RMXX, "rmxx", },
1163 { ESCAPE_SMUL, "smul", },
1164 { ESCAPE_RMUL, "rmul", },
1165 { ESCAPE_SC, "sc", },
1166 { ESCAPE_RC, "rc", },
1167 { ESCAPE_IND, "ind", },
1168 { ESCAPE_INDN, "indn", },
1169 { ESCAPE_CLEAR, "clear", },
1170 { ESCAPE_OC, "oc", },
1171 { ESCAPE_RMKX, "rmkx", },
1172 { ESCAPE_INITC, "initc", },
1173 { ESCAPE_MAX, NULL, },
1174 };
1175 for(typeof(*strtdescs)* strtdesc = strtdescs ; strtdesc->esc < ESCAPE_MAX ; ++strtdesc){
1176 if(init_terminfo_esc(ti, strtdesc->tinfo, strtdesc->esc, tablelen, tableused)){
1177 return -1;
1178 }
1179 }
1180 // verify that the terminal provides cursor addressing (absolute movement)
1181 if(ti->escindices[ESCAPE_CUP] == 0){
1182 logpanic("required terminfo capability 'cup' not defined");
1183 return -1;
1184 }
1185 return 0;
1186}
1187
1188// handle any terminal query responses.
1189static int
1190handle_responses(tinfo* ti, size_t* tablelen, size_t* tableused,
1191 int* cursor_y, int* cursor_x, unsigned draininput,
1192 unsigned* kitty_graphics){
1193 struct initial_responses* iresp;
1194 if((iresp = inputlayer_get_responses(ti->ictx)) == NULL){
1195 goto err;
1196 }
1197 if(ti->termversion){
1198 free(iresp->version);
1199 }else{
1200 ti->termversion = iresp->version; // takes ownership
1201 }
1202 if(iresp->appsync_supported){
1203 if(add_appsync_escapes_sm(ti, tablelen, tableused)){
1204 free(iresp->hpa);
1205 goto err;
1206 }
1207 }
1208 if(iresp->hpa){
1209 if(grow_esc_table(ti, iresp->hpa, ESCAPE_HPA, tablelen, tableused)){
1210 free(iresp->hpa);
1211 goto err;
1212 }
1213 free(iresp->hpa);
1214 }
1215 if((ti->kbdlevel = iresp->kbdlevel) == UINT_MAX){
1216 ti->kbdlevel = 0;
1217 if(!draininput){
1218 if(tty_emit(XTMODKEYS, ti->ttyfd) < 0){
1219 goto err;
1220 }
1221 }
1222 }else{
1223 ti->kittykbdsupport = true;
1224 }
1225 if(iresp->qterm != TERMINAL_UNKNOWN){
1226 ti->qterm = iresp->qterm;
1227 }
1228 *cursor_y = iresp->cursory;
1229 *cursor_x = iresp->cursorx;
1230 if(iresp->dimy && iresp->dimx){
1231 // FIXME probably oughtn't be setting the defaults, as this is just some
1232 // random transient measurement?
1233 ti->default_rows = iresp->dimy;
1234 ti->default_cols = iresp->dimx;
1235 ti->dimy = iresp->dimy;
1236 ti->dimx = iresp->dimx;
1237 }
1238 if(iresp->maxpaletteread >= 0){
1239 memcpy(ti->originalpalette.chans, iresp->palette.chans,
1240 sizeof(*ti->originalpalette.chans) * (iresp->maxpaletteread + 1));
1241 ti->maxpaletteread = iresp->maxpaletteread;
1242 }
1243 if(iresp->rgb){
1244 ti->caps.rgb = true;
1245 }
1246 if(iresp->pixy && iresp->pixx){
1247 ti->pixy = iresp->pixy;
1248 ti->pixx = iresp->pixx;
1249 }
1250 if(ti->default_rows && ti->default_cols){
1251 ti->cellpxy = ti->pixy / ti->default_rows;
1252 ti->cellpxx = ti->pixx / ti->default_cols;
1253 }
1254 if(iresp->got_bg){
1255 // reset the 0xfe000000 we loaded during initialization. if we're
1256 // kitty, we'll add the 0x01000000 in during heuristics.
1257 ti->bg_collides_default = iresp->bg;
1258 }
1259 if(iresp->got_fg){
1260 ti->fg_default = iresp->fg;
1261 }
1262 // kitty trumps sixel, when both are available
1263 if((*kitty_graphics = iresp->kitty_graphics) == 0){
1266 }
1267 ti->sixel_maxy_pristine = iresp->sixely;
1268 ti->sixel_maxy = iresp->sixely;
1269 ti->sixel_maxx = iresp->sixelx;
1270 }
1271 ti->pixelmice = iresp->pixelmice;
1272 if(iresp->rectangular_edits){
1273 if(grow_esc_table(ti, "\x1b[%p1%d;%p2%d;%p3%d;$z", ESCAPE_DECERA, tablelen, tableused)){
1274 goto err;
1275 }
1276 }
1277 free(iresp);
1278 return 0;
1279
1280err:
1281 free(iresp);
1282 return -1;
1283}
1284
1285// if |termtype| is not NULL, it is used to look up the terminfo database entry
1286// via setupterm(). the value of the TERM environment variable is otherwise
1287// (implicitly) used. some details are not exposed via terminfo, and we must
1288// make heuristic decisions based on the detected terminal type, yuck :/.
1289// the first thing we do is fire off any queries we have (XTSMGRAPHICS, etc.)
1290// with a trailing Device Attributes. all known terminals will reply to a
1291// Device Attributes, allowing us to get a negative response if our queries
1292// aren't supported by the terminal. we fire it off early because we have a
1293// full round trip before getting the reply, which is likely to pace init.
1294int interrogate_terminfo(tinfo* ti, FILE* out, unsigned utf8,
1295 unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts,
1296 int* cursor_y, int* cursor_x, ncsharedstats* stats,
1297 int lmargin, int tmargin, int rmargin, int bmargin,
1298 unsigned draininput){
1299 // if a specified termtype was provided in the notcurses_options, it was
1300 // loaded into our environment at TERM.
1301 const char* termtype = getenv("TERM");
1302 int foolcursor_x, foolcursor_y;
1303 if(!cursor_x){
1304 cursor_x = &foolcursor_x;
1305 }
1306 if(!cursor_y){
1307 cursor_y = &foolcursor_y;
1308 }
1309 *cursor_x = *cursor_y = -1;
1310 ti->sixelengine = NULL;
1311 ti->bg_collides_default = 0xfe000000;
1312 ti->fg_default = 0xff000000;
1313 ti->kbdlevel = UINT_MAX; // see comment in tinfo definition
1314 ti->maxpaletteread = -1;
1315 ti->qterm = TERMINAL_UNKNOWN;
1316 // we don't need a controlling tty for everything we do; allow a failure here
1317 ti->ttyfd = get_tty_fd(out);
1318 ti->gpmfd = -1;
1319 size_t tablelen = 0;
1320 size_t tableused = 0;
1321 const char* tname = NULL;
1322#ifdef __APPLE__
1323 ti->qterm = macos_early_matches();
1324#elif defined(__MINGW32__)
1325 if(termtype){
1326 logwarn("termtype (%s) ignored on windows", termtype);
1327 }
1328 if(prepare_windows_terminal(ti, &tablelen, &tableused)){
1329 logpanic("failed opening Windows ConPTY");
1330 return -1;
1331 }
1332#else
1333 ti->qterm = unix_early_matches(termtype);
1334#if defined(__linux__)
1335 ti->linux_fb_fd = -1;
1336 ti->linux_fbuffer = MAP_FAILED;
1337 // we might or might not program quadrants into the console font
1338 if(is_linux_console(ti->ttyfd)){
1339 ti->qterm = TERMINAL_LINUX;
1340 }
1341#endif
1342#endif
1343 if(ti->ttyfd >= 0){
1344 if((ti->tpreserved = calloc(1, sizeof(*ti->tpreserved))) == NULL){
1345 return -1;
1346 }
1347 if(tcgetattr(ti->ttyfd, ti->tpreserved)){
1348 logpanic("couldn't preserve terminal state for %d (%s)", ti->ttyfd, strerror(errno));
1349 free(ti->tpreserved);
1350 return -1;
1351 }
1352 // enter cbreak mode regardless of user preference until we've performed
1353 // terminal interrogation. at that point, we might restore original mode.
1354 if(cbreak_mode(ti)){
1355 free(ti->tpreserved);
1356 return -1;
1357 }
1358 // if we already know our terminal (e.g. on the linux console), there's no
1359 // need to send the identification queries. the controls are sufficient.
1360 bool minimal = (ti->qterm != TERMINAL_UNKNOWN);
1361 if(send_initial_queries(ti, minimal, noaltscreen, draininput)){
1362 goto err;
1363 }
1364 }
1365#ifndef __MINGW32__
1366 // windows doesn't really have a concept of terminfo. you might ssh into other
1367 // machines, but they'll use the terminfo installed thereon (putty, etc.).
1368 int termerr;
1369 if(setupterm(termtype, ti->ttyfd, &termerr)){
1370 logpanic("terminfo error %d for [%s] (see terminfo(3ncurses))",
1371 termerr, termtype ? termtype : "");
1372 goto err;
1373 }
1374 tname = termname(); // longname() is also available
1375#endif
1376 int linesigs_enabled = 1;
1377 if(ti->tpreserved){
1378 if(!(ti->tpreserved->c_lflag & ISIG)){
1379 linesigs_enabled = 0;
1380 }
1381 }
1382 if(init_inputlayer(ti, stdin, lmargin, tmargin, rmargin, bmargin,
1383 stats, draininput, linesigs_enabled)){
1384 goto err;
1385 }
1386 ti->sprixel_scale_height = 1;
1387 get_default_geometry(ti);
1388 ti->caps.utf8 = utf8;
1389 // allow the "rgb" boolean terminfo capability, a COLORTERM environment
1390 // variable of either "truecolor" or "24bit", or unconditionally enable it
1391 // for several terminals known to always support 8bpc rgb setaf/setab.
1392 if(ti->caps.colors == 0){
1393 int colors = tigetnum("colors");
1394 if(colors <= 0){
1395 ti->caps.colors = 1;
1396 }else{
1397 ti->caps.colors = colors;
1398 }
1399 ti->caps.rgb = query_rgb(); // independent of colors
1400 }
1401 if(do_terminfo_lookups(ti, &tablelen, &tableused)){
1402 goto err;
1403 }
1404 if(ti->ttyfd >= 0){
1405 // if the keypad needn't be explicitly enabled, smkx is not present
1406 const char* smkx = get_escape(ti, ESCAPE_SMKX);
1407 if(smkx){
1408 if(tty_emit(tiparm(smkx), ti->ttyfd) < 0){
1409 logpanic("error enabling keypad transmit mode");
1410 goto err;
1411 }
1412 }
1413 }
1414 if(tigetflag("bce") > 0){
1415 ti->bce = true;
1416 }
1417 if(ti->caps.colors > 1){
1418 const char* initc = get_escape(ti, ESCAPE_INITC);
1419 if(initc){
1420 ti->caps.can_change_colors = true;
1421 }
1422 }else{ // disable initc if there's no color support
1423 ti->escindices[ESCAPE_INITC] = 0;
1424 }
1425 // neither of these is supported on e.g. the "linux" virtual console.
1426 if(!noaltscreen){
1427 if(init_terminfo_esc(ti, "smcup", ESCAPE_SMCUP, &tablelen, &tableused) ||
1428 init_terminfo_esc(ti, "rmcup", ESCAPE_RMCUP, &tablelen, &tableused)){
1429 goto err;
1430 }
1431 const char* smcup = get_escape(ti, ESCAPE_SMCUP);
1432 if(smcup){
1433 ti->in_alt_screen = 1;
1434 // if we're not using the standard smcup, our initial hardcoded use of it
1435 // presumably had no effect; warn the user.
1436 if(strcmp(smcup, SMCUP)){
1437 logwarn("warning: non-standard smcup!");
1438 }
1439 }
1440 }else{
1441 ti->escindices[ESCAPE_SMCUP] = 0;
1442 ti->escindices[ESCAPE_RMCUP] = 0;
1443 }
1444 if(get_escape(ti, ESCAPE_CIVIS) == NULL){
1445 char* chts;
1446 if(terminfostr(&chts, "chts") == 0){
1447 if(grow_esc_table(ti, chts, ESCAPE_CIVIS, &tablelen, &tableused)){
1448 goto err;
1449 }
1450 }
1451 }
1452 if(get_escape(ti, ESCAPE_BOLD)){
1453 if(grow_esc_table(ti, "\e[22m", ESCAPE_NOBOLD, &tablelen, &tableused)){
1454 goto err;
1455 }
1456 }
1457 // if op is defined as ansi 39 + ansi 49, make the split definitions
1458 // available. this ought be asserted by extension capability "ax", but
1459 // no terminal i've found seems to do so. =[
1460 const char* op = get_escape(ti, ESCAPE_OP);
1461 if(op && strcmp(op, "\x1b[39;49m") == 0){
1462 if(grow_esc_table(ti, "\x1b[39m", ESCAPE_FGOP, &tablelen, &tableused) ||
1463 grow_esc_table(ti, "\x1b[49m", ESCAPE_BGOP, &tablelen, &tableused)){
1464 goto err;
1465 }
1466 }
1467 unsigned kitty_graphics = 0;
1468 if(ti->ttyfd >= 0){
1469 if(handle_responses(ti, &tablelen, &tableused, cursor_y, cursor_x,
1470 draininput, &kitty_graphics)){
1471 goto err;
1472 }
1473 if(nocbreak){
1474 // FIXME do this in input later, upon signaling completion?
1475 if(tcsetattr(ti->ttyfd, TCSANOW, ti->tpreserved)){
1476 goto err;
1477 }
1478 }
1479 }else{
1480 ti->kbdlevel = 0; // confirmed no support, don't bother popping
1481 }
1482 // now look up any terminfo elements we might not have received via requests
1483 if(ti->escindices[ESCAPE_HPA] == 0){
1484 if(init_terminfo_esc(ti, "hpa", ESCAPE_HPA, &tablelen, &tableused)){
1485 goto err;
1486 }
1487 }
1488 if(*cursor_x >= 0 && *cursor_y >= 0){
1489 if(add_u7_escape(ti, &tablelen, &tableused)){
1490 goto err;
1491 }
1492 }
1493 bool forcesdm = false;
1494 bool invertsixel = false;
1495 if(apply_term_heuristics(ti, tname, ti->qterm, &tablelen, &tableused,
1496 &forcesdm, &invertsixel, nonewfonts)){
1497 goto err;
1498 }
1499 build_supported_styles(ti);
1500 if(ti->pixel_draw == NULL && ti->pixel_draw_late == NULL){
1501 // color_registers was only assigned if kitty_graphics were unavailable
1502 if(ti->color_registers > 0){
1503 setup_sixel_bitmaps(ti, ti->ttyfd, forcesdm, invertsixel);
1504 }
1505 if(kitty_graphics){
1506 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_STATIC);
1507 }
1508 }
1509 return 0;
1510
1511err:
1512 if(ti->ttyfd >= 0){
1513 // if we haven't yet received a reply confirming lack of kitty keyboard
1514 // support, it'll be UINT_MAX, and we ought try to pop (in case we died
1515 // following the keyboard set, but before confirming support).
1516 if(ti->kbdlevel){
1517 tty_emit(KKEYBOARD_POP, ti->ttyfd);
1518 }
1519 tty_emit(RMCUP, ti->ttyfd);
1520 }
1521 if(ti->tpreserved){
1522 (void)tcsetattr(ti->ttyfd, TCSANOW, ti->tpreserved);
1523 free(ti->tpreserved);
1524 ti->tpreserved = NULL;
1525 }
1526 stop_inputlayer(ti);
1527 free(ti->esctable);
1528 free(ti->termversion);
1529 del_curterm(cur_term);
1530 close(ti->ttyfd);
1531 ti->ttyfd = -1;
1532 return -1;
1533}
1534
1535char* termdesc_longterm(const tinfo* ti){
1536 size_t tlen = strlen(ti->termname) + 1;
1537 size_t slen = tlen;
1538 if(ti->termversion){
1539 slen += strlen(ti->termversion) + 1;
1540 }
1541 char* ret = malloc(slen);
1542 if(ret){
1543 memcpy(ret, ti->termname, tlen);
1544 if(ti->termversion){
1545 ret[tlen - 1] = ' ';
1546 strcpy(ret + tlen, ti->termversion);
1547 }
1548 }
1549 return ret;
1550}
1551
1552// send a u7 request, and wait until we have a cursor report. if input's ttyfd
1553// is valid, we can just camp there. otherwise, we need dance with potential
1554// user input looking at infd. note that we do not use Windows's
1555// GetConsoleScreenBufferInfo() because it is unreliable for this purpose
1556// when the viewing area is not aligned with the forward edge of the buffer,
1557// and also due to negative interactions with ssh.
1558int locate_cursor(tinfo* ti, unsigned* cursor_y, unsigned* cursor_x){
1559 const char* u7 = get_escape(ti, ESCAPE_U7);
1560 if(u7 == NULL){
1561 logwarn("no support in terminfo");
1562 return -1;
1563 }
1564 if(ti->ttyfd < 0){
1565 logwarn("no valid path for cursor report");
1566 return -1;
1567 }
1568 int fd = ti->ttyfd;
1569 if(get_cursor_location(ti->ictx, u7, cursor_y, cursor_x)){
1570 return -1;
1571 }
1572 loginfo("got a report from %d %d/%d", fd, *cursor_y, *cursor_x);
1573 return 0;
1574}
1575
1576int tiocgwinsz(int fd, struct winsize* ws){
1577#ifndef __MINGW32__
1578 int i = ioctl(fd, TIOCGWINSZ, ws);
1579 if(i < 0){
1580 logerror("TIOCGWINSZ failed on %d (%s)", fd, strerror(errno));
1581 return -1;
1582 }
1583 if(ws->ws_row <= 0 || ws->ws_col <= 0){
1584 logerror("bogon from TIOCGWINSZ on %d (%d/%d)",
1585 fd, ws->ws_row, ws->ws_col);
1586 return -1;
1587 }
1588#else
1589 (void)fd;
1590 (void)ws;
1591#endif
1592 return 0;
1593}
1594
1596#ifndef __MINGW32__
1597 int ttyfd = ti->ttyfd;
1598 if(ttyfd < 0){
1599 return 0;
1600 }
1601 // assume it's not a true terminal (e.g. we might be redirected to a file)
1602 struct termios modtermios;
1603 memcpy(&modtermios, ti->tpreserved, sizeof(modtermios));
1604 // see termios(3). disabling ECHO and ICANON means input will not be echoed
1605 // to the screen, input is made available without enter-based buffering, and
1606 // line editing is disabled. since we have not gone into raw mode, ctrl+c
1607 // etc. still have their typical effects. ICRNL maps return to 13 (Ctrl+M)
1608 // instead of 10 (Ctrl+J).
1609 modtermios.c_lflag &= (~ECHO & ~ICANON);
1610 modtermios.c_iflag &= ~ICRNL;
1611 if(tcsetattr(ttyfd, TCSANOW, &modtermios)){
1612 logerror("error disabling echo / canonical on %d (%s)", ttyfd, strerror(errno));
1613 return -1;
1614 }
1615#else
1616 // we don't yet have a way to take Cygwin/MSYS2 out of canonical mode FIXME.
1617 DWORD mode;
1618 if(!GetConsoleMode(ti->inhandle, &mode)){
1619 logerror("error acquiring input mode");
1620 return -1;
1621 }
1622 mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
1623 if(!SetConsoleMode(ti->inhandle, mode)){
1624 logerror("error setting input mode");
1625 return -1;
1626 }
1627#endif
1628 return 0;
1629}
1630
1631// replace or populate the TERM environment variable with 'termname'
1632int putenv_term(const char* tname){
1633 #define ENVVAR "TERM"
1634 const char* oldterm = getenv(ENVVAR);
1635 if(oldterm){
1636 logdebug("replacing %s value %s with %s", ENVVAR, oldterm, tname);
1637 }else{
1638 loginfo("provided %s value %s", ENVVAR, tname);
1639 }
1640 if(oldterm && strcmp(oldterm, tname) == 0){
1641 return 0;
1642 }
1643 char* buf = malloc(strlen(tname) + strlen(ENVVAR) + 1);
1644 if(buf == NULL){
1645 return -1;
1646 }
1647 int c = putenv(buf);
1648 if(c){
1649 logerror("couldn't export %s", buf);
1650 }
1651 free(buf);
1652 return c;
1653}
void set_pixel_blitter(ncblitter blitfxn)
Definition blit.c:1289
assert(false)
const nccell * c
Definition egcpool.h:296
uint32_t idx
Definition egcpool.h:298
free(duplicated)
int get_tty_fd(FILE *ttyfp)
Definition fd.c:455
int init_inputlayer(tinfo *ti, FILE *infp, int lmargin, int tmargin, int rmargin, int bmargin, ncsharedstats *stats, unsigned drain, int linesigs_enabled)
Definition in.c:2627
struct initial_responses * inputlayer_get_responses(inputctx *ictx)
Definition in.c:2896
int get_cursor_location(inputctx *ictx, const char *u7, unsigned *y, unsigned *x)
Definition in.c:2785
int stop_inputlayer(tinfo *ti)
Definition in.c:2644
queried_terminals_e
Definition in.h:31
@ TERMINAL_XTERM
Definition in.h:38
@ TERMINAL_LINUX
Definition in.h:35
@ TERMINAL_CONTOUR
Definition in.h:47
@ TERMINAL_APPLE
Definition in.h:50
@ TERMINAL_UNKNOWN
Definition in.h:32
@ TERMINAL_ALACRITTY
Definition in.h:46
@ TERMINAL_GNUSCREEN
Definition in.h:44
@ TERMINAL_TERMINOLOGY
Definition in.h:49
@ TERMINAL_MSTERMINAL
Definition in.h:52
@ TERMINAL_RXVT
Definition in.h:51
@ TERMINAL_KONSOLE
Definition in.h:54
@ TERMINAL_MLTERM
Definition in.h:42
@ TERMINAL_KITTY
Definition in.h:40
@ TERMINAL_MINTTY
Definition in.h:53
@ TERMINAL_ITERM
Definition in.h:48
@ TERMINAL_WEZTERM
Definition in.h:45
@ TERMINAL_VTE
Definition in.h:39
@ TERMINAL_FOOT
Definition in.h:41
@ TERMINAL_TMUX
Definition in.h:43
int kitty_remove(int id, fbuf *f)
Definition kitty.c:1130
int kitty_move(sprixel *s, fbuf *f, unsigned noscroll, int yoff, int xoff)
Definition kitty.c:1194
int kitty_wipe(sprixel *s, int ycell, int xcell)
Definition kitty.c:448
int kitty_scrub(const ncpile *p, sprixel *s)
Definition kitty.c:1139
int kitty_blit_selfref(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
Definition kitty.c:1124
int kitty_rebuild_selfref(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
Definition kitty.c:950
int kitty_clear_all(fbuf *f)
Definition kitty.c:1210
int kitty_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
Definition kitty.c:219
int kitty_rebuild_animation(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
Definition kitty.c:972
int kitty_blit(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
Definition kitty.c:1112
int kitty_draw(const tinfo *ti, const ncpile *p, sprixel *s, fbuf *f, int yoff, int xoff)
Definition kitty.c:1170
uint8_t * kitty_trans_auxvec(const ncpile *p)
Definition kitty.c:342
int kitty_wipe_animation(sprixel *s, int ycell, int xcell)
Definition kitty.c:394
int kitty_commit(fbuf *f, sprixel *s, unsigned noscroll)
Definition kitty.c:534
int kitty_wipe_selfref(sprixel *s, int ycell, int xcell)
Definition kitty.c:410
int kitty_blit_animated(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
Definition kitty.c:1118
int fbcon_scrub(const struct ncpile *p, sprixel *s)
Definition linux.c:113
int fbcon_draw(const tinfo *ti, sprixel *s, int y, int x)
Definition linux.c:799
int fbcon_blit(struct ncplane *n, int linesize, const void *data, int leny, int lenx, const struct blitterargs *bargs)
Definition linux.c:44
void fbcon_scroll(const struct ncpile *p, tinfo *ti, int rows)
Definition linux.c:807
int fbcon_wipe(sprixel *s, int ycell, int xcell)
Definition linux.c:16
int fbcon_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
Definition linux.c:791
bool is_linux_console(int fd)
bool is_linux_framebuffer(struct tinfo *ti)
int reprogram_console_font(struct tinfo *ti, unsigned no_font_changes, bool *halfblocks, bool *quadrants)
#define logerror(fmt,...)
Definition logging.h:32
#define loginfo(fmt,...)
Definition logging.h:42
#define logdebug(fmt,...)
Definition logging.h:52
#define logwarn(fmt,...)
Definition logging.h:37
#define logpanic(fmt,...)
Definition logging.h:22
#define NCSTYLE_UNDERCURL
Definition notcurses.h:772
#define NCSTYLE_UNDERLINE
Definition notcurses.h:771
#define NCSTYLE_ITALIC
Definition notcurses.h:770
#define NCSTYLE_BOLD
Definition notcurses.h:773
#define NCSTYLE_STRUCK
Definition notcurses.h:774
ncpixelimpl_e
Definition notcurses.h:1672
@ NCPIXEL_KITTY_STATIC
Definition notcurses.h:1680
@ NCPIXEL_KITTY_SELFREF
Definition notcurses.h:1689
@ NCPIXEL_KITTY_ANIMATED
Definition notcurses.h:1685
@ NCPIXEL_SIXEL
Definition notcurses.h:1674
@ NCPIXEL_LINUXFB
Definition notcurses.h:1675
API int API int const nccell unsigned len
Definition notcurses.h:2588
uint8_t * sixel_trans_auxvec(const ncpile *p)
Definition sixel.c:1786
int sixel_scrub(const ncpile *p, sprixel *s)
Definition sixel.c:1439
void sixel_refresh(const ncpile *p, sprixel *s)
Definition sixel.c:725
int sixel_init(tinfo *ti, int fd)
Definition sixel.c:1602
int sixel_init_inverted(tinfo *ti, int fd)
Definition sixel.c:1592
int sixel_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
Definition sixel.c:1724
void sixel_cleanup(tinfo *ti)
Definition sixel.c:1764
int sixel_draw(const tinfo *ti, const ncpile *p, sprixel *s, fbuf *f, int yoff, int xoff)
Definition sixel.c:1477
int sixel_wipe(sprixel *s, int ycell, int xcell)
Definition sixel.c:655
int sixel_blit(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
Definition sixel.c:1389
int sixel_init_forcesdm(tinfo *ti, int fd)
Definition sixel.c:1588
int sprite_init(tinfo *t, int fd)
Definition sprite.c:214
#define SIXEL_MAX_REGISTERS
Definition sprite.h:12
queried_terminals_e qterm
Definition in.h:63
int maxpaletteread
Definition in.h:84
unsigned appsync_supported
Definition in.h:62
ncpalette palette
Definition in.h:83
bool pixelmice
Definition in.h:85
unsigned kbdlevel
Definition in.h:82
bool rectangular_edits
Definition in.h:70
char * version
Definition in.h:81
unsigned kitty_graphics
Definition in.h:64
int cursorx
Definition in.h:61
int cursory
Definition in.h:60
uint32_t fg
Definition in.h:66
bool got_fg
Definition in.h:68
bool got_bg
Definition in.h:67
uint32_t bg
Definition in.h:65
char * hpa
Definition in.h:86
int color_registers
Definition in.h:78
unsigned colors
Definition notcurses.h:1637
bool can_change_colors
Definition notcurses.h:1640
uint32_t chans[NCPALETTESIZE]
Definition notcurses.h:1585
queried_terminals_e qterm
Definition termdesc.h:178
unsigned supported_styles
Definition termdesc.h:120
int maxpaletteread
Definition termdesc.h:193
ncpixelimpl_e pixel_implementation
Definition termdesc.h:134
void * sixelengine
Definition termdesc.h:175
unsigned cellpxx
Definition termdesc.h:117
int(* pixel_init)(struct tinfo *ti, int fd)
Definition termdesc.h:146
const char * termname
Definition termdesc.h:176
int(* pixel_draw_late)(const struct tinfo *, struct sprixel *s, int yoff, int xoff)
Definition termdesc.h:149
unsigned gratuitous_hpa
Definition termdesc.h:184
int(* pixel_remove)(int id, fbuf *f)
Definition termdesc.h:145
bool bce
Definition termdesc.h:218
int(* pixel_rebuild)(struct sprixel *s, int y, int x, uint8_t *auxvec)
Definition termdesc.h:140
unsigned pixx
Definition termdesc.h:113
bool pixelmice
Definition termdesc.h:197
int gpmfd
Definition termdesc.h:195
unsigned pixy
Definition termdesc.h:112
uint32_t bg_collides_default
Definition termdesc.h:127
unsigned kbdlevel
Definition termdesc.h:216
int(* pixel_wipe)(struct sprixel *s, int y, int x)
Definition termdesc.h:138
unsigned dimx
Definition termdesc.h:118
void(* pixel_cleanup)(struct tinfo *)
Definition termdesc.h:158
int(* pixel_commit)(fbuf *f, struct sprixel *s, unsigned noscroll)
Definition termdesc.h:155
int(* pixel_scrub)(const struct ncpile *p, struct sprixel *s)
Definition termdesc.h:152
ncpalette originalpalette
Definition termdesc.h:192
bool kittykbdsupport
Definition termdesc.h:217
unsigned sprixel_scale_height
Definition termdesc.h:174
nccapabilities caps
Definition termdesc.h:111
struct termios * tpreserved
Definition termdesc.h:180
bool in_alt_screen
Definition termdesc.h:219
char * esctable
Definition termdesc.h:110
void(* pixel_refresh)(const struct ncpile *p, struct sprixel *s)
Definition termdesc.h:144
unsigned sixel_maxx
Definition termdesc.h:165
uint32_t fg_default
Definition termdesc.h:130
int default_rows
Definition termdesc.h:189
int(* pixel_draw)(const struct tinfo *, const struct ncpile *p, struct sprixel *s, fbuf *f, int y, int x)
Definition termdesc.h:147
uint8_t *(* pixel_trans_auxvec)(const struct ncpile *p)
Definition termdesc.h:159
unsigned dimy
Definition termdesc.h:118
unsigned sixel_maxy
Definition termdesc.h:172
int(* pixel_clear_all)(fbuf *f)
Definition termdesc.h:153
char * termversion
Definition termdesc.h:177
unsigned sixel_maxy_pristine
Definition termdesc.h:173
struct inputctx * ictx
Definition termdesc.h:181
int(* pixel_move)(struct sprixel *s, fbuf *f, unsigned noscroll, int yoff, int xoff)
Definition termdesc.h:151
int default_cols
Definition termdesc.h:190
void(* pixel_scroll)(const struct ncpile *p, struct tinfo *, int rows)
Definition termdesc.h:157
unsigned cellpxy
Definition termdesc.h:116
int color_registers
Definition termdesc.h:164
int ttyfd
Definition termdesc.h:109
uint16_t escindices[ESCAPE_MAX]
Definition termdesc.h:108
int enter_alternate_screen(int fd, FILE *ttyfp, tinfo *ti, unsigned drain)
Definition termdesc.c:559
#define SMCUP
Definition termdesc.c:472
#define KKBDENTER
Definition termdesc.c:465
int grow_esc_table(tinfo *ti, const char *tstr, escape_e esc, size_t *tlen, size_t *tused)
Definition termdesc.c:14
int putenv_term(const char *tname)
Definition termdesc.c:1632
#define DIRECTIVES
Definition termdesc.c:446
#define DSRCPR
Definition termdesc.c:423
#define XTMODKEYS
Definition termdesc.c:400
#define PQUERYBUFLEN
int locate_cursor(tinfo *ti, unsigned *cursor_y, unsigned *cursor_x)
Definition termdesc.c:1558
void free_terminfo_cache(tinfo *ti)
Definition termdesc.c:197
int interrogate_terminfo(tinfo *ti, FILE *out, unsigned utf8, unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts, int *cursor_y, int *cursor_x, ncsharedstats *stats, int lmargin, int tmargin, int rmargin, int bmargin, unsigned draininput)
Definition termdesc.c:1294
#define ENVVAR
int cbreak_mode(tinfo *ti)
Definition termdesc.c:1595
int leave_alternate_screen(int fd, FILE *fp, tinfo *ti, unsigned drain)
Definition termdesc.c:611
char * termdesc_longterm(const tinfo *ti)
Definition termdesc.c:1535
#define IDQUERIES
Definition termdesc.c:405
#define RMCUP
Definition termdesc.c:473
int tiocgwinsz(int fd, struct winsize *ws)
Definition termdesc.c:1576
return NULL
Definition termdesc.h:229
escape_e
Definition termdesc.h:44
@ ESCAPE_CUU
Definition termdesc.h:59
@ ESCAPE_RMKX
Definition termdesc.h:66
@ ESCAPE_CIVIS
Definition termdesc.h:54
@ ESCAPE_SC
Definition termdesc.h:78
@ ESCAPE_BOLD
Definition termdesc.h:62
@ ESCAPE_CUP
Definition termdesc.h:45
@ ESCAPE_CUD
Definition termdesc.h:64
@ ESCAPE_CNORM
Definition termdesc.h:55
@ ESCAPE_MAX
Definition termdesc.h:90
@ ESCAPE_SMULX
Definition termdesc.h:73
@ ESCAPE_SETAB
Definition termdesc.h:49
@ ESCAPE_RESTORECOLORS
Definition termdesc.h:88
@ ESCAPE_CUF
Definition termdesc.h:61
@ ESCAPE_IND
Definition termdesc.h:76
@ ESCAPE_NOBOLD
Definition termdesc.h:63
@ ESCAPE_VPA
Definition termdesc.h:47
@ ESCAPE_SMXX
Definition termdesc.h:70
@ ESCAPE_ESUM
Definition termdesc.h:86
@ ESCAPE_OP
Definition termdesc.h:50
@ ESCAPE_OC
Definition termdesc.h:56
@ ESCAPE_RMCUP
Definition termdesc.h:69
@ ESCAPE_SMCUP
Definition termdesc.h:68
@ ESCAPE_SMKX
Definition termdesc.h:65
@ ESCAPE_FGOP
Definition termdesc.h:51
@ ESCAPE_SITM
Definition termdesc.h:57
@ ESCAPE_SAVECOLORS
Definition termdesc.h:87
@ ESCAPE_RMXX
Definition termdesc.h:75
@ ESCAPE_DECERA
Definition termdesc.h:89
@ ESCAPE_INDN
Definition termdesc.h:77
@ ESCAPE_BSUM
Definition termdesc.h:85
@ ESCAPE_SMUL
Definition termdesc.h:71
@ ESCAPE_CLEAR
Definition termdesc.h:80
@ ESCAPE_INITC
Definition termdesc.h:81
@ ESCAPE_RITM
Definition termdesc.h:58
@ ESCAPE_SETAF
Definition termdesc.h:48
@ ESCAPE_U7
Definition termdesc.h:82
@ ESCAPE_BGOP
Definition termdesc.h:52
@ ESCAPE_HPA
Definition termdesc.h:46
@ ESCAPE_RMUL
Definition termdesc.h:72
@ ESCAPE_RC
Definition termdesc.h:79
@ ESCAPE_SMULNOX
Definition termdesc.h:74
@ ESCAPE_EL
Definition termdesc.h:67
@ ESCAPE_CUB
Definition termdesc.h:60
@ ESCAPE_SGR0
Definition termdesc.h:53
#define XTMODKEYSUNDO
Definition termdesc.h:27
#define KKEYBOARD_POP
Definition termdesc.h:22
static escape_e e
Definition termdesc.h:224
int prepare_windows_terminal(struct tinfo *ti, size_t *tablelen, size_t *tableused)