Notcurses 3.0.16
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 // FIXME we have problems using SELFREF; fix them!
745 /*if(compare_versions(ti->termversion, "0.22.1") >= 0){
746 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_SELFREF);
747 }else*/ if(compare_versions(ti->termversion, "0.20.0") >= 0){
748 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_ANIMATED);
749 // XTPOPCOLORS didn't reliably work until a bugfix late in 0.23.1 (see
750 // https://github.com/kovidgoyal/kitty/issues/4351), so reprogram the
751 // font directly until we exceed that version.
752 if(compare_versions(ti->termversion, "0.23.1") > 0){
753 if(add_pushcolors_escapes(ti, tablelen, tableused)){
754 return NULL;
755 }
756 }
757 }else{
758 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_STATIC);
759 }
760 // kitty SUM doesn't want long sequences, which is exactly where we use
761 // it. remove support (we pick it up from queries).
762 kill_appsync_escapes(ti);
763 ti->gratuitous_hpa = true;
764 return "Kitty";
765}
766
767static const char*
768apply_alacritty_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
769 bool* forcesdm, bool* invertsixel){
770 ti->caps.quadrants = true;
771 // ti->caps.sextants = true; // alacritty https://github.com/alacritty/alacritty/issues/4409
772 ti->caps.rgb = true;
773 // Alacritty implements DCS ASU, but no detection for it
774 if(add_appsync_escapes_dcs(ti, tablelen, tableused)){
775 return NULL;
776 }
777 *forcesdm = true;
778 if(compare_versions(ti->termversion, "0.15.1") < 0){
779 *invertsixel = true;
780 }
781 return "Alacritty";
782}
783
784static const char*
785apply_vte_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused){
786 ti->caps.quadrants = true;
787 ti->caps.sextants = true; // VTE has long enjoyed good sextant support
788 if(add_smulx_escapes(ti, tablelen, tableused)){
789 return NULL;
790 }
791 // VTE understands DSC ACU, but doesn't do anything with it; don't use it
792 return "VTE";
793}
794
795static const char*
796apply_foot_heuristics(tinfo* ti, size_t *tablelen, size_t *tableused,
797 bool* forcesdm, bool* invertsixel){
798 ti->caps.sextants = true;
799 ti->caps.quadrants = true;
800 ti->caps.rgb = true;
801 *forcesdm = true;
802 if(compare_versions(ti->termversion, "1.8.2") < 0){
803 *invertsixel = true;
804 }
805 if(compare_versions(ti->termversion, "1.18.0") >= 0){
806 if(add_smulx_escapes(ti, tablelen, tableused)){
807 return NULL;
808 }
809 }
810 if(compare_versions(ti->termversion, "1.20.0") >= 0){
811 ti->caps.octants = true;
812 }
813 return "foot";
814}
815
816static const char*
817apply_gnuscreen_heuristics(tinfo* ti){
818 if(compare_versions(ti->termversion, "5.0") < 0){
819 ti->caps.rgb = false;
820 }
821 return "GNU screen";
822}
823
824static const char*
825apply_mlterm_heuristics(tinfo* ti){
826 ti->caps.quadrants = true; // good caps.quadrants, no caps.sextants as of 3.9.0
827 return "MLterm";
828}
829
830static const char*
831apply_wezterm_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused){
832 ti->caps.rgb = true;
833 ti->caps.quadrants = true;
834 if(ti->termversion && strcmp(ti->termversion, "20210610") >= 0){
835 ti->caps.sextants = true; // good caps.sextants as of 2021-06-10
836 if(add_smulx_escapes(ti, tablelen, tableused)){
837 return NULL;
838 }
839 }
840 return "WezTerm";
841}
842
843static const char*
844apply_xterm_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
845 bool* forcesdm, bool* invertsixel){
846 *forcesdm = true;
847 if(compare_versions(ti->termversion, "369") < 0){
848 *invertsixel = true; // xterm 369 inverted DECSDM
849 }
850 // xterm 357 added color palette escapes XT{PUSH,POP,REPORT}COLORS
851 if(compare_versions(ti->termversion, "357") >= 0){
852 if(add_pushcolors_escapes(ti, tablelen, tableused)){
853 return NULL;
854 }
855 }
856 return "XTerm";
857}
858
859static const char*
860apply_mintty_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
861 bool* forcesdm, bool* invertsixel){
862 if(add_smulx_escapes(ti, tablelen, tableused)){
863 return NULL;
864 }
865 *forcesdm = true;
866 if(compare_versions(ti->termversion, "3.5.2") < 0){
867 *invertsixel = true;
868 }
869 ti->bce = true;
870 return "MinTTY";
871}
872
873static const char*
874apply_msterminal_heuristics(tinfo* ti){
875 ti->caps.rgb = true;
876 ti->caps.quadrants = true;
877 return "Windows ConHost";
878}
879
880static const char*
881apply_contour_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused,
882 bool* forcesdm, bool* invertsixel){
883 if(add_smulx_escapes(ti, tablelen, tableused)){
884 return NULL;
885 }
886 if(add_pushcolors_escapes(ti, tablelen, tableused)){
887 return NULL;
888 }
889 ti->caps.quadrants = true;
890 ti->caps.sextants = true;
891 ti->caps.rgb = true;
892 *forcesdm = true;
893 *invertsixel = false;
894 return "Contour";
895}
896
897static const char*
898apply_iterm_heuristics(tinfo* ti, size_t* tablelen, size_t* tableused){
899 // iTerm implements DCS ASU, but has no detection for it
900 if(add_appsync_escapes_dcs(ti, tablelen, tableused)){
901 return NULL;
902 }
903 ti->caps.quadrants = true;
904 ti->caps.rgb = true;
905 return "iTerm2";
906}
907
908static const char*
909apply_rxvt_heuristics(tinfo* ti){
910 ti->caps.braille = false;
911 ti->caps.quadrants = true;
912 return "RXVT";
913}
914
915static const char*
916apply_terminology_heuristics(tinfo* ti){
917 // no RGB as of at least 1.9.0
918 ti->caps.quadrants = true;
919 return "Terminology";
920}
921
922static const char*
923apply_konsole_heuristics(tinfo* ti){
924 ti->caps.rgb = true;
925 ti->caps.quadrants = true;
926 return "Konsole";
927}
928
929static const char*
930apply_ghostty_heuristics(tinfo* ti){
931 ti->caps.quadrants = true;
932 ti->caps.sextants = true;
933 return "ghostty";
934}
935
936static const char*
937apply_linux_heuristics(tinfo* ti, unsigned nonewfonts){
938 const char* tname = NULL;
939#ifdef __linux__
940 struct utsname un;
941 if(uname(&un) == 0){
942 ti->termversion = strdup(un.release);
943 }
944 if(is_linux_framebuffer(ti)){
945 tname = "FBcon";
946 setup_fbcon_bitmaps(ti, ti->linux_fb_fd);
947 }else{
948 tname = "VT";
949 }
950 ti->caps.halfblocks = false;
951 ti->caps.braille = false; // no caps.braille, no caps.sextants in linux console
952 if(ti->ttyfd >= 0){
953 reprogram_console_font(ti, nonewfonts, &ti->caps.halfblocks,
954 &ti->caps.quadrants);
955 }
956 // assume no useful unicode drawing unless we're positively sure
957#else
958 (void)ti;
959 (void)nonewfonts;
960#endif
961 return tname;
962}
963
964// qui si convien lasciare ogne sospetto; ogne viltà convien che qui sia morta.
965// in a more perfect world, this function would not exist, but this is a
966// regrettably imperfect world, and thus all manner of things are not maintained
967// in terminfo, and old terminfos abound, and users don't understand terminfo,
968// so we override and/or supply various properties based on terminal
969// identification performed earlier. we still get most things from terminfo,
970// though, so it's something of a worst-of-all-worlds deal where TERM still
971// needs be correct, even though we identify the terminal. le sigh.
972static int
973apply_term_heuristics(tinfo* ti, const char* tname, queried_terminals_e qterm,
974 size_t* tablelen, size_t* tableused,
975 bool* forcesdm, bool* invertsixel,
976 unsigned nonewfonts){
977#ifdef __MINGW32__
978 if(qterm == TERMINAL_UNKNOWN){
979 qterm = TERMINAL_MSTERMINAL;
980 }
981#endif
982 if(!tname){
983 // setupterm interprets a missing/empty TERM variable as the special value “unknown”.
984 tname = ti->termname ? ti->termname : "unknown";
985 }
986 loginfo("tname is %s (qterm %d)", tname, qterm);
987 // st had neither caps.sextants nor caps.quadrants last i checked (0.8.4)
988 ti->caps.braille = true; // most everyone has working caps.braille, even from fonts
989 ti->caps.halfblocks = true; // most everyone has working halfblocks
990 const char* newname = NULL;
991 switch(qterm){
992 case TERMINAL_KITTY:
993 newname = apply_kitty_heuristics(ti, tablelen, tableused);
994 break;
996 newname = apply_alacritty_heuristics(ti, tablelen, tableused,
997 forcesdm, invertsixel);
998 break;
999 case TERMINAL_VTE:
1000 newname = apply_vte_heuristics(ti, tablelen, tableused);
1001 break;
1002 case TERMINAL_FOOT:
1003 newname = apply_foot_heuristics(ti, tablelen, tableused,
1004 forcesdm, invertsixel);
1005 break;
1006 case TERMINAL_TMUX:
1007 newname = "tmux"; // FIXME what, oh what to do with tmux?
1008 break;
1009 case TERMINAL_GNUSCREEN:
1010 newname = apply_gnuscreen_heuristics(ti);
1011 break;
1012 case TERMINAL_MLTERM:
1013 newname = apply_mlterm_heuristics(ti);
1014 break;
1015 case TERMINAL_WEZTERM:
1016 newname = apply_wezterm_heuristics(ti, tablelen, tableused);
1017 break;
1018 case TERMINAL_XTERM:
1019 newname = apply_xterm_heuristics(ti, tablelen, tableused,
1020 forcesdm, invertsixel);
1021 break;
1022 case TERMINAL_MINTTY:
1023 newname = apply_mintty_heuristics(ti, tablelen, tableused,
1024 forcesdm, invertsixel);
1025 break;
1027 newname = apply_msterminal_heuristics(ti);
1028 break;
1029 case TERMINAL_CONTOUR:
1030 newname = apply_contour_heuristics(ti, tablelen, tableused,
1031 forcesdm, invertsixel);
1032 break;
1033 case TERMINAL_ITERM:
1034 newname = apply_iterm_heuristics(ti, tablelen, tableused);
1035 break;
1036 case TERMINAL_RXVT:
1037 newname = apply_rxvt_heuristics(ti);
1038 break;
1039 case TERMINAL_APPLE:
1040 newname = "Terminal.app"; // no quadrants, no sextants, no rgb, but it does have braille
1041 break;
1042 case TERMINAL_LINUX:
1043 newname = apply_linux_heuristics(ti, nonewfonts);
1044 break;
1046 newname = apply_terminology_heuristics(ti);
1047 break;
1048 case TERMINAL_KONSOLE:
1049 newname = apply_konsole_heuristics(ti);
1050 break;
1051 case TERMINAL_GHOSTTY:
1052 newname = apply_ghostty_heuristics(ti);
1053 break;
1054 default:
1055 logwarn("no match for qterm %d tname %s", qterm, tname);
1056 newname = tname;
1057 break;
1058 }
1059 if(newname == NULL){
1060 logerror("no name provided for termtype %d", qterm);
1061 return -1;
1062 }
1063 tname = newname;
1064 // run a wcwidth(⣿) to guarantee libc Unicode 3 support, independent of term
1065 if(wcwidth(L'⣿') < 0){
1066 ti->caps.braille = false;
1067 }
1068 // run a wcwidth(🬸) to guarantee libc Unicode 13 support, independent of term
1069 if(wcwidth(L'🬸') < 0){
1070 ti->caps.sextants = false;
1071 }
1072 // run a wcwidth(𜴀) to guarantee libc Unicode 16 support, independent of term
1073 if(wcwidth(L'𜴀') < 0){
1074 ti->caps.octants = false;
1075 }
1076 ti->termname = tname;
1077 return 0;
1078}
1079
1080// some terminals cannot combine certain styles with colors, as expressed in
1081// the "ncv" terminfo capability (using ncurses-style constants). don't
1082// advertise support for the style in that case. otherwise, if the style is
1083// supported, OR it into supported_styles (using Notcurses-style constants).
1084static void
1085build_supported_styles(tinfo* ti){
1086 const struct style {
1087 unsigned s; // NCSTYLE_* value
1088 int esc; // ESCAPE_* value for enable
1089 const char* tinfo; // terminfo capability for conditional permit
1090 unsigned ncvbit; // bit in "ncv" mask for unconditional deny
1091 } styles[] = {
1092 { NCSTYLE_BOLD, ESCAPE_BOLD, "bold", A_BOLD },
1093 { NCSTYLE_UNDERLINE, ESCAPE_SMUL, "smul", A_UNDERLINE },
1094 { NCSTYLE_ITALIC, ESCAPE_SITM, "sitm", A_ITALIC },
1095 { NCSTYLE_STRUCK, ESCAPE_SMXX, "smxx", 0 },
1096 { NCSTYLE_UNDERCURL, ESCAPE_SMULX, "Smulx", 0 },
1097 { 0, 0, NULL, 0 }
1098 };
1099 int nocolor_stylemask = tigetnum("ncv");
1100 for(typeof(*styles)* s = styles ; s->s ; ++s){
1101 if(get_escape(ti, s->esc)){
1102 if(nocolor_stylemask > 0){
1103 if(nocolor_stylemask & s->ncvbit){
1104 ti->escindices[s->esc] = 0;
1105 continue;
1106 }
1107 }
1108 ti->supported_styles |= s->s;
1109 }
1110 }
1111}
1112
1113#ifdef __APPLE__
1114// Terminal.App is a wretched piece of shit that can't handle even the most
1115// basic of queries, instead bleeding them through to stdout like a great
1116// wounded hippopotamus. it does export "TERM_PROGRAM=Apple_Terminal", becuase
1117// it is a committee on sewage and drainage where all the members have
1118// tourette's. on mac os, if TERM_PROGRAM=Apple_Terminal, accept this hideous
1119// existence, circumvent all queries, and may god have mercy on our souls.
1120// of course that means if a terminal launched from Terminal.App doesn't clear
1121// or reset this environment variable, they're cursed to live as Terminal.App.
1122// i'm likewise unsure what we're supposed to do should you ssh anywhere =[.
1124macos_early_matches(void){
1125 const char* tp = getenv("TERM_PROGRAM");
1126 if(tp == NULL){
1127 return TERMINAL_UNKNOWN;
1128 }
1129 if(strcmp(tp, "Apple_Terminal")){
1130 return TERMINAL_UNKNOWN;
1131 }
1132 return TERMINAL_APPLE;
1133}
1134#endif
1135
1136#ifndef __APPLE__
1137#ifndef __MINGW32__
1138// rxvt has a deeply fucked up palette code implementation. its responses are
1139// terminated with a bare ESC instead of BEL or ST, impossible to encode in
1140// our automaton alongside the proper flow. its "oc" doesn't reset the palette,
1141// meaning we must preserve and reload it ourselves. there's no way to identify
1142// rxvt via query, so if we get it in TERM, set up our automaton for its fubar
1143// replies, and don't bother sending any identification requests.
1145unix_early_matches(const char* term){
1146 if(term == NULL){
1147 return TERMINAL_UNKNOWN;
1148 }
1149 // urxvt likewise declares TERM=rxvt-whatever
1150 if(strncmp(term, "rxvt", 4) == 0){
1151 return TERMINAL_RXVT;
1152 }
1153 return TERMINAL_UNKNOWN;
1154}
1155#endif
1156#endif
1157
1158static int
1159do_terminfo_lookups(tinfo *ti, size_t* tablelen, size_t* tableused){
1160 // don't list any here for which we also send XTGETTCAP sequences
1161 const struct strtdesc {
1162 escape_e esc;
1163 const char* tinfo;
1164 } strtdescs[] = {
1165 { ESCAPE_CUP, "cup", },
1166 { ESCAPE_VPA, "vpa", },
1167 // Not all terminals support setting the fore/background independently
1168 { ESCAPE_SETAF, "setaf", },
1169 { ESCAPE_SETAB, "setab", },
1170 { ESCAPE_OP, "op", },
1171 { ESCAPE_CNORM, "cnorm", },
1172 { ESCAPE_CIVIS, "civis", },
1173 { ESCAPE_SGR0, "sgr0", },
1174 { ESCAPE_SITM, "sitm", },
1175 { ESCAPE_RITM, "ritm", },
1176 { ESCAPE_BOLD, "bold", },
1177 { ESCAPE_CUD, "cud", },
1178 { ESCAPE_CUU, "cuu", },
1179 { ESCAPE_CUF, "cuf", },
1180 { ESCAPE_CUB, "cub", },
1181 { ESCAPE_U7, "u7", },
1182 { ESCAPE_SMKX, "smkx", },
1183 { ESCAPE_SMXX, "smxx", },
1184 { ESCAPE_EL, "el", },
1185 { ESCAPE_RMXX, "rmxx", },
1186 { ESCAPE_SMUL, "smul", },
1187 { ESCAPE_RMUL, "rmul", },
1188 { ESCAPE_SC, "sc", },
1189 { ESCAPE_RC, "rc", },
1190 { ESCAPE_IND, "ind", },
1191 { ESCAPE_INDN, "indn", },
1192 { ESCAPE_CLEAR, "clear", },
1193 { ESCAPE_OC, "oc", },
1194 { ESCAPE_RMKX, "rmkx", },
1195 { ESCAPE_INITC, "initc", },
1196 { ESCAPE_MAX, NULL, },
1197 };
1198 for(typeof(*strtdescs)* strtdesc = strtdescs ; strtdesc->esc < ESCAPE_MAX ; ++strtdesc){
1199 if(init_terminfo_esc(ti, strtdesc->tinfo, strtdesc->esc, tablelen, tableused)){
1200 return -1;
1201 }
1202 }
1203 // verify that the terminal provides cursor addressing (absolute movement)
1204 if(ti->escindices[ESCAPE_CUP] == 0){
1205 logpanic("required terminfo capability 'cup' not defined");
1206 return -1;
1207 }
1208 return 0;
1209}
1210
1211// handle any terminal query responses.
1212static int
1213handle_responses(tinfo* ti, size_t* tablelen, size_t* tableused,
1214 int* cursor_y, int* cursor_x, unsigned draininput,
1215 unsigned* kitty_graphics){
1216 struct initial_responses* iresp;
1217 if((iresp = inputlayer_get_responses(ti->ictx)) == NULL){
1218 goto err;
1219 }
1220 if(ti->termversion){
1221 free(iresp->version);
1222 }else{
1223 ti->termversion = iresp->version; // takes ownership
1224 }
1225 if(iresp->appsync_supported){
1226 if(add_appsync_escapes_sm(ti, tablelen, tableused)){
1227 free(iresp->hpa);
1228 goto err;
1229 }
1230 }
1231 if(iresp->hpa){
1232 if(grow_esc_table(ti, iresp->hpa, ESCAPE_HPA, tablelen, tableused)){
1233 free(iresp->hpa);
1234 goto err;
1235 }
1236 free(iresp->hpa);
1237 }
1238 if((ti->kbdlevel = iresp->kbdlevel) == UINT_MAX){
1239 ti->kbdlevel = 0;
1240 if(!draininput){
1241 if(tty_emit(XTMODKEYS, ti->ttyfd) < 0){
1242 goto err;
1243 }
1244 }
1245 }else{
1246 ti->kittykbdsupport = true;
1247 }
1248 if(iresp->qterm != TERMINAL_UNKNOWN){
1249 ti->qterm = iresp->qterm;
1250 }
1251 *cursor_y = iresp->cursory;
1252 *cursor_x = iresp->cursorx;
1253 if(iresp->dimy && iresp->dimx){
1254 // FIXME probably oughtn't be setting the defaults, as this is just some
1255 // random transient measurement?
1256 ti->default_rows = iresp->dimy;
1257 ti->default_cols = iresp->dimx;
1258 ti->dimy = iresp->dimy;
1259 ti->dimx = iresp->dimx;
1260 }
1261 if(iresp->maxpaletteread >= 0){
1262 memcpy(ti->originalpalette.chans, iresp->palette.chans,
1263 sizeof(*ti->originalpalette.chans) * (iresp->maxpaletteread + 1));
1264 ti->maxpaletteread = iresp->maxpaletteread;
1265 }
1266 if(iresp->rgb){
1267 ti->caps.rgb = true;
1268 }
1269 if(iresp->pixy && iresp->pixx){
1270 ti->pixy = iresp->pixy;
1271 ti->pixx = iresp->pixx;
1272 }
1273 if(ti->default_rows && ti->default_cols){
1274 ti->cellpxy = ti->pixy / ti->default_rows;
1275 ti->cellpxx = ti->pixx / ti->default_cols;
1276 }
1277 if(iresp->got_bg){
1278 // reset the 0xfe000000 we loaded during initialization. if we're
1279 // kitty, we'll add the 0x01000000 in during heuristics.
1280 ti->bg_collides_default = iresp->bg;
1281 }
1282 if(iresp->got_fg){
1283 ti->fg_default = iresp->fg;
1284 }
1285 // kitty trumps sixel, when both are available
1286 if((*kitty_graphics = iresp->kitty_graphics) == 0){
1289 }
1290 ti->sixel_maxy_pristine = iresp->sixely;
1291 ti->sixel_maxy = iresp->sixely;
1292 ti->sixel_maxx = iresp->sixelx;
1293 }
1294 ti->pixelmice = iresp->pixelmice;
1295 if(iresp->rectangular_edits){
1296 if(grow_esc_table(ti, "\x1b[%p1%d;%p2%d;%p3%d;$z", ESCAPE_DECERA, tablelen, tableused)){
1297 goto err;
1298 }
1299 }
1300 free(iresp);
1301 return 0;
1302
1303err:
1304 free(iresp);
1305 return -1;
1306}
1307
1308// if |termtype| is not NULL, it is used to look up the terminfo database entry
1309// via setupterm(). the value of the TERM environment variable is otherwise
1310// (implicitly) used. some details are not exposed via terminfo, and we must
1311// make heuristic decisions based on the detected terminal type, yuck :/.
1312// the first thing we do is fire off any queries we have (XTSMGRAPHICS, etc.)
1313// with a trailing Device Attributes. all known terminals will reply to a
1314// Device Attributes, allowing us to get a negative response if our queries
1315// aren't supported by the terminal. we fire it off early because we have a
1316// full round trip before getting the reply, which is likely to pace init.
1317int interrogate_terminfo(tinfo* ti, FILE* out, unsigned utf8,
1318 unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts,
1319 int* cursor_y, int* cursor_x, ncsharedstats* stats,
1320 int lmargin, int tmargin, int rmargin, int bmargin,
1321 unsigned draininput){
1322 // if a specified termtype was provided in the notcurses_options, it was
1323 // loaded into our environment at TERM.
1324 const char* termtype = getenv("TERM");
1325 int foolcursor_x, foolcursor_y;
1326 if(!cursor_x){
1327 cursor_x = &foolcursor_x;
1328 }
1329 if(!cursor_y){
1330 cursor_y = &foolcursor_y;
1331 }
1332 *cursor_x = *cursor_y = -1;
1333 ti->sixelengine = NULL;
1334 ti->bg_collides_default = 0xfe000000;
1335 ti->fg_default = 0xff000000;
1336 ti->kbdlevel = UINT_MAX; // see comment in tinfo definition
1337 ti->maxpaletteread = -1;
1338 ti->qterm = TERMINAL_UNKNOWN;
1339 // we don't need a controlling tty for everything we do; allow a failure here
1340 ti->ttyfd = get_tty_fd(out);
1341 ti->gpmfd = -1;
1342 size_t tablelen = 0;
1343 size_t tableused = 0;
1344 const char* tname = NULL;
1345#ifdef __APPLE__
1346 ti->qterm = macos_early_matches();
1347#elif defined(__MINGW32__)
1348 if(termtype){
1349 logwarn("termtype (%s) ignored on windows", termtype);
1350 }
1351 if(prepare_windows_terminal(ti, &tablelen, &tableused)){
1352 logpanic("failed opening Windows ConPTY");
1353 return -1;
1354 }
1355#else
1356 ti->qterm = unix_early_matches(termtype);
1357#if defined(__linux__)
1358 ti->linux_fb_fd = -1;
1359 ti->linux_fbuffer = MAP_FAILED;
1360 // we might or might not program quadrants into the console font
1361 if(is_linux_console(ti->ttyfd)){
1362 ti->qterm = TERMINAL_LINUX;
1363 }
1364#endif
1365#endif
1366 if(ti->ttyfd >= 0){
1367 if((ti->tpreserved = calloc(1, sizeof(*ti->tpreserved))) == NULL){
1368 return -1;
1369 }
1370 if(tcgetattr(ti->ttyfd, ti->tpreserved)){
1371 logpanic("couldn't preserve terminal state for %d (%s)", ti->ttyfd, strerror(errno));
1372 free(ti->tpreserved);
1373 return -1;
1374 }
1375 // enter cbreak mode regardless of user preference until we've performed
1376 // terminal interrogation. at that point, we might restore original mode.
1377 if(cbreak_mode(ti)){
1378 free(ti->tpreserved);
1379 return -1;
1380 }
1381 // if we already know our terminal (e.g. on the linux console), there's no
1382 // need to send the identification queries. the controls are sufficient.
1383 bool minimal = (ti->qterm != TERMINAL_UNKNOWN);
1384 if(send_initial_queries(ti, minimal, noaltscreen, draininput)){
1385 goto err;
1386 }
1387 }
1388#ifndef __MINGW32__
1389 // windows doesn't really have a concept of terminfo. you might ssh into other
1390 // machines, but they'll use the terminfo installed thereon (putty, etc.).
1391 int termerr;
1392 if(setupterm(termtype, ti->ttyfd, &termerr)){
1393 logpanic("terminfo error %d for [%s] (see terminfo(3ncurses))",
1394 termerr, termtype ? termtype : "");
1395 goto err;
1396 }
1397 tname = termname(); // longname() is also available
1398#endif
1399 int linesigs_enabled = 1;
1400 if(ti->tpreserved){
1401 if(!(ti->tpreserved->c_lflag & ISIG)){
1402 linesigs_enabled = 0;
1403 }
1404 }
1405 if(init_inputlayer(ti, stdin, lmargin, tmargin, rmargin, bmargin,
1406 stats, draininput, linesigs_enabled)){
1407 goto err;
1408 }
1409 ti->sprixel_scale_height = 1;
1410 get_default_geometry(ti);
1411 ti->caps.utf8 = utf8;
1412 // allow the "rgb" boolean terminfo capability, a COLORTERM environment
1413 // variable of either "truecolor" or "24bit", or unconditionally enable it
1414 // for several terminals known to always support 8bpc rgb setaf/setab.
1415 if(ti->caps.colors == 0){
1416 int colors = tigetnum("colors");
1417 if(colors <= 0){
1418 ti->caps.colors = 1;
1419 }else{
1420 ti->caps.colors = colors;
1421 }
1422 ti->caps.rgb = query_rgb(); // independent of colors
1423 }
1424 if(do_terminfo_lookups(ti, &tablelen, &tableused)){
1425 goto err;
1426 }
1427 if(ti->ttyfd >= 0){
1428 // if the keypad needn't be explicitly enabled, smkx is not present
1429 const char* smkx = get_escape(ti, ESCAPE_SMKX);
1430 if(smkx){
1431 if(tty_emit(tiparm(smkx), ti->ttyfd) < 0){
1432 logpanic("error enabling keypad transmit mode");
1433 goto err;
1434 }
1435 }
1436 }
1437 if(tigetflag("bce") > 0){
1438 ti->bce = true;
1439 }
1440 if(ti->caps.colors > 1){
1441 const char* initc = get_escape(ti, ESCAPE_INITC);
1442 if(initc){
1443 ti->caps.can_change_colors = true;
1444 }
1445 }else{ // disable initc if there's no color support
1446 ti->escindices[ESCAPE_INITC] = 0;
1447 }
1448 // neither of these is supported on e.g. the "linux" virtual console.
1449 if(!noaltscreen){
1450 if(init_terminfo_esc(ti, "smcup", ESCAPE_SMCUP, &tablelen, &tableused) ||
1451 init_terminfo_esc(ti, "rmcup", ESCAPE_RMCUP, &tablelen, &tableused)){
1452 goto err;
1453 }
1454 const char* smcup = get_escape(ti, ESCAPE_SMCUP);
1455 if(smcup){
1456 ti->in_alt_screen = 1;
1457 // if we're not using the standard smcup, our initial hardcoded use of it
1458 // presumably had no effect; warn the user.
1459 if(strcmp(smcup, SMCUP)){
1460 logwarn("warning: non-standard smcup!");
1461 }
1462 }
1463 }else{
1464 ti->escindices[ESCAPE_SMCUP] = 0;
1465 ti->escindices[ESCAPE_RMCUP] = 0;
1466 }
1467 if(get_escape(ti, ESCAPE_CIVIS) == NULL){
1468 char* chts;
1469 if(terminfostr(&chts, "chts") == 0){
1470 if(grow_esc_table(ti, chts, ESCAPE_CIVIS, &tablelen, &tableused)){
1471 goto err;
1472 }
1473 }
1474 }
1475 if(get_escape(ti, ESCAPE_BOLD)){
1476 if(grow_esc_table(ti, "\e[22m", ESCAPE_NOBOLD, &tablelen, &tableused)){
1477 goto err;
1478 }
1479 }
1480 // if op is defined as ansi 39 + ansi 49, make the split definitions
1481 // available. this ought be asserted by extension capability "ax", but
1482 // no terminal i've found seems to do so. =[
1483 const char* op = get_escape(ti, ESCAPE_OP);
1484 if(op && strcmp(op, "\x1b[39;49m") == 0){
1485 if(grow_esc_table(ti, "\x1b[39m", ESCAPE_FGOP, &tablelen, &tableused) ||
1486 grow_esc_table(ti, "\x1b[49m", ESCAPE_BGOP, &tablelen, &tableused)){
1487 goto err;
1488 }
1489 }
1490 unsigned kitty_graphics = 0;
1491 if(ti->ttyfd >= 0){
1492 if(handle_responses(ti, &tablelen, &tableused, cursor_y, cursor_x,
1493 draininput, &kitty_graphics)){
1494 goto err;
1495 }
1496 if(nocbreak){
1497 // FIXME do this in input later, upon signaling completion?
1498 if(tcsetattr(ti->ttyfd, TCSANOW, ti->tpreserved)){
1499 goto err;
1500 }
1501 }
1502 }else{
1503 ti->kbdlevel = 0; // confirmed no support, don't bother popping
1504 }
1505 // now look up any terminfo elements we might not have received via requests
1506 if(ti->escindices[ESCAPE_HPA] == 0){
1507 if(init_terminfo_esc(ti, "hpa", ESCAPE_HPA, &tablelen, &tableused)){
1508 goto err;
1509 }
1510 }
1511 if(*cursor_x >= 0 && *cursor_y >= 0){
1512 if(add_u7_escape(ti, &tablelen, &tableused)){
1513 goto err;
1514 }
1515 }
1516 bool forcesdm = false;
1517 bool invertsixel = false;
1518 if(apply_term_heuristics(ti, tname, ti->qterm, &tablelen, &tableused,
1519 &forcesdm, &invertsixel, nonewfonts)){
1520 goto err;
1521 }
1522 build_supported_styles(ti);
1523 if(ti->pixel_draw == NULL && ti->pixel_draw_late == NULL){
1524 // color_registers was only assigned if kitty_graphics were unavailable
1525 if(ti->color_registers > 0){
1526 setup_sixel_bitmaps(ti, ti->ttyfd, forcesdm, invertsixel);
1527 }
1528 if(kitty_graphics){
1529 setup_kitty_bitmaps(ti, ti->ttyfd, NCPIXEL_KITTY_STATIC);
1530 }
1531 }
1532 return 0;
1533
1534err:
1535 if(ti->ttyfd >= 0){
1536 // if we haven't yet received a reply confirming lack of kitty keyboard
1537 // support, it'll be UINT_MAX, and we ought try to pop (in case we died
1538 // following the keyboard set, but before confirming support).
1539 if(ti->kbdlevel){
1540 tty_emit(KKEYBOARD_POP, ti->ttyfd);
1541 }
1542 tty_emit(RMCUP, ti->ttyfd);
1543 }
1544 if(ti->tpreserved){
1545 (void)tcsetattr(ti->ttyfd, TCSANOW, ti->tpreserved);
1546 free(ti->tpreserved);
1547 ti->tpreserved = NULL;
1548 }
1549 stop_inputlayer(ti);
1550 free(ti->esctable);
1551 free(ti->termversion);
1552 del_curterm(cur_term);
1553 close(ti->ttyfd);
1554 ti->ttyfd = -1;
1555 return -1;
1556}
1557
1558char* termdesc_longterm(const tinfo* ti){
1559 size_t tlen = strlen(ti->termname) + 1;
1560 size_t slen = tlen;
1561 if(ti->termversion){
1562 slen += strlen(ti->termversion) + 1;
1563 }
1564 char* ret = malloc(slen);
1565 if(ret){
1566 memcpy(ret, ti->termname, tlen);
1567 if(ti->termversion){
1568 ret[tlen - 1] = ' ';
1569 strcpy(ret + tlen, ti->termversion);
1570 }
1571 }
1572 return ret;
1573}
1574
1575// send a u7 request, and wait until we have a cursor report. if input's ttyfd
1576// is valid, we can just camp there. otherwise, we need dance with potential
1577// user input looking at infd. note that we do not use Windows's
1578// GetConsoleScreenBufferInfo() because it is unreliable for this purpose
1579// when the viewing area is not aligned with the forward edge of the buffer,
1580// and also due to negative interactions with ssh.
1581int locate_cursor(tinfo* ti, unsigned* cursor_y, unsigned* cursor_x){
1582 const char* u7 = get_escape(ti, ESCAPE_U7);
1583 if(u7 == NULL){
1584 logwarn("no support in terminfo");
1585 return -1;
1586 }
1587 if(ti->ttyfd < 0){
1588 logwarn("no valid path for cursor report");
1589 return -1;
1590 }
1591 int fd = ti->ttyfd;
1592 if(get_cursor_location(ti->ictx, u7, cursor_y, cursor_x)){
1593 return -1;
1594 }
1595 loginfo("got a report from %d %d/%d", fd, *cursor_y, *cursor_x);
1596 return 0;
1597}
1598
1599int tiocgwinsz(int fd, struct winsize* ws){
1600#ifndef __MINGW32__
1601 int i = ioctl(fd, TIOCGWINSZ, ws);
1602 if(i < 0){
1603 logerror("TIOCGWINSZ failed on %d (%s)", fd, strerror(errno));
1604 return -1;
1605 }
1606 if(ws->ws_row <= 0 || ws->ws_col <= 0){
1607 logerror("bogon from TIOCGWINSZ on %d (%d/%d)",
1608 fd, ws->ws_row, ws->ws_col);
1609 return -1;
1610 }
1611#else
1612 (void)fd;
1613 (void)ws;
1614#endif
1615 return 0;
1616}
1617
1619#ifndef __MINGW32__
1620 int ttyfd = ti->ttyfd;
1621 if(ttyfd < 0){
1622 return 0;
1623 }
1624 // assume it's not a true terminal (e.g. we might be redirected to a file)
1625 struct termios modtermios;
1626 memcpy(&modtermios, ti->tpreserved, sizeof(modtermios));
1627 // see termios(3). disabling ECHO and ICANON means input will not be echoed
1628 // to the screen, input is made available without enter-based buffering, and
1629 // line editing is disabled. since we have not gone into raw mode, ctrl+c
1630 // etc. still have their typical effects. ICRNL maps return to 13 (Ctrl+M)
1631 // instead of 10 (Ctrl+J).
1632 modtermios.c_lflag &= (~ECHO & ~ICANON);
1633 modtermios.c_iflag &= ~ICRNL;
1634 if(tcsetattr(ttyfd, TCSANOW, &modtermios)){
1635 logerror("error disabling echo / canonical on %d (%s)", ttyfd, strerror(errno));
1636 return -1;
1637 }
1638#else
1639 // we don't yet have a way to take Cygwin/MSYS2 out of canonical mode FIXME.
1640 DWORD mode;
1641 if(!GetConsoleMode(ti->inhandle, &mode)){
1642 logerror("error acquiring input mode");
1643 return -1;
1644 }
1645 mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
1646 if(!SetConsoleMode(ti->inhandle, mode)){
1647 logerror("error setting input mode");
1648 return -1;
1649 }
1650#endif
1651 return 0;
1652}
1653
1654// replace or populate the TERM environment variable with 'termname'
1655int putenv_term(const char* tname){
1656 #define ENVVAR "TERM"
1657 const char* oldterm = getenv(ENVVAR);
1658 if(oldterm){
1659 logdebug("replacing %s value %s with %s", ENVVAR, oldterm, tname);
1660 }else{
1661 loginfo("provided %s value %s", ENVVAR, tname);
1662 }
1663 if(oldterm && strcmp(oldterm, tname) == 0){
1664 return 0;
1665 }
1666 char* buf = malloc(strlen(tname) + strlen(ENVVAR) + 1);
1667 if(buf == NULL){
1668 return -1;
1669 }
1670 int c = putenv(buf);
1671 if(c){
1672 logerror("couldn't export %s", buf);
1673 }
1674 free(buf);
1675 return c;
1676}
void set_pixel_blitter(ncblitter blitfxn)
Definition blit.c:1289
const nccell * c
Definition egcpool.h:207
uint32_t idx
Definition egcpool.h:209
assert(r >=0)
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:2638
struct initial_responses * inputlayer_get_responses(inputctx *ictx)
Definition in.c:2921
int get_cursor_location(inputctx *ictx, const char *u7, unsigned *y, unsigned *x)
Definition in.c:2810
int stop_inputlayer(tinfo *ti)
Definition in.c:2655
queried_terminals_e
Definition in.h:31
@ TERMINAL_XTERM
Definition in.h:38
@ TERMINAL_GHOSTTY
Definition in.h:55
@ 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:2592
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:64
int maxpaletteread
Definition in.h:85
unsigned appsync_supported
Definition in.h:63
ncpalette palette
Definition in.h:84
bool pixelmice
Definition in.h:86
unsigned kbdlevel
Definition in.h:83
bool rectangular_edits
Definition in.h:71
char * version
Definition in.h:82
unsigned kitty_graphics
Definition in.h:65
int cursorx
Definition in.h:62
int cursory
Definition in.h:61
uint32_t fg
Definition in.h:67
bool got_fg
Definition in.h:69
bool got_bg
Definition in.h:68
uint32_t bg
Definition in.h:66
char * hpa
Definition in.h:87
int color_registers
Definition in.h:79
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:1655
#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:1581
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:1317
#define ENVVAR
int cbreak_mode(tinfo *ti)
Definition termdesc.c:1618
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:1558
#define IDQUERIES
Definition termdesc.c:405
#define RMCUP
Definition termdesc.c:473
int tiocgwinsz(int fd, struct winsize *ws)
Definition termdesc.c:1599
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)