5#include <sys/utsname.h>
15 size_t* tlen,
size_t* tused){
19 fprintf(stderr,
"Can't add escape %d to full table\n", esc);
22 if(get_escape(ti, esc)){
23 fprintf(stderr,
"Already defined escape %d (%s)\n",
24 esc, get_escape(ti, esc));
27 size_t slen = strlen(tstr) + 1;
28 if(*tlen - *tused < slen){
30 size_t newsize = *tlen + 4020 + slen;
31 char* tmp = realloc(ti->
esctable, newsize);
39 memcpy(ti->
esctable + *tused, tstr, slen);
56get_default_dimension(
const char* envvar,
const char* tinfovar,
int def){
57 const char* env = getenv(envvar);
65 num = tigetnum(tinfovar);
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",
78 ti->default_cols, ti->default_cols != 1 ?
"s" :
"");
87setup_sixel_bitmaps(
tinfo* ti,
int fd,
unsigned forcesdm,
unsigned invert80){
157setup_fbcon_bitmaps(
tinfo* ti,
int fd){
178 bool rgb = (tigetflag(
"RGB") > 0 || tigetflag(
"Tc") > 0);
191 const char* cterm = getenv(
"COLORTERM");
192 rgb = cterm && (strcmp(cterm,
"truecolor") == 0 || strcmp(cterm,
"24bit") == 0);
199 loginfo(
"brought down input layer");
206 if(ti->linux_fb_fd >= 0){
207 close(ti->linux_fb_fd);
209 free(ti->linux_fb_dev);
210 if(ti->linux_fbuffer != MAP_FAILED){
211 munmap(ti->linux_fbuffer, ti->linux_fb_len);
215 loginfo(
"destroyed terminfo cache");
223compare_versions(
const char* restrict v1,
const char* restrict v2){
227 const char* v1e = v1;
228 const char* v2e = v2;
230 long v1v = strtol(v1, (
char **)&v1e, 10);
231 long v2v = strtol(v2, (
char **)&v2e, 10);
232 if(v1e == v1 && v2e == v2){
244 if(*v1e !=
'.' && *v2e !=
'.'){
246 }
else if(*v1e !=
'.' || *v2e !=
'.'){
276terminfostr(
char** gseq,
const char* name){
277 *gseq = tigetstr(name);
278 if(*gseq ==
NULL || *gseq == (
char*)-1){
289 bool indelay =
false;
291 for(
char* cur = *gseq ; *cur ; ++cur){
318 size_t* tablelen,
size_t* tableused){
323 if(terminfostr(&tstr, name) == 0){
336#define TRIDEVATTR "\x1b[=c"
351#define PRIDEVATTR "\x1b[c"
354#define XTVERSION "\x1b[>0q"
364#define XTGETTCAP "\x1bP+q544e;524742;687061\x1b\\"
373#define SECDEVATTR "\x1b[>c"
383#define KITTYQUERY "\x1b_Gi=1,a=q;\x1b\\"
390#define KKBDSUPPORT "\x1b[=27u"
395#define KKBDQUERY "\x1b[?u"
400#define XTMODKEYS "\x1b[>2;1m\x1b[>4;2m"
405#define IDQUERIES TRIDEVATTR \
414#define DEFBGQ "\x1b]11;?\e\\"
415#define DEFFGQ "\x1b]10;?\e\\"
423#define DSRCPR "\x1b[6n"
427#define SUMQUERY "\x1b[?2026$p"
430#define PIXELMOUSEQUERY "\x1b[?1016$p"
433#define CREGSXTSM "\x1b[?2;1;0S"
436#define GEOMXTSM "\x1b[?1;1;0S"
439#define GEOMPIXEL "\x1b[14t"
442#define GEOMCELL "\x1b[18t"
446#define DIRECTIVES DEFFGQ \
461#define KKEYBOARD_PUSH "\x1b[>u"
465#define KKBDENTER KKEYBOARD_PUSH KKBDSUPPORT
472#define SMCUP DECSET(SET_SMCUP)
473#define RMCUP DECRST(SET_SMCUP)
479#define PQUERYBUFLEN 4096
484 if(pqueries ==
NULL){
489 const int qsets[] = { 0, 8, 16, 88, 256 };
490 for(
size_t q = 1 ; q <
sizeof(qsets) /
sizeof(*qsets) ; ++q){
492 for(
int i = qsets[q - 1] ; i < qsets[q] ; ++i){
493 len += sprintf(pqueries +
len,
"\x1b]4;%d;?\e\\", i);
496 if(blocking_write(fd, pqueries,
len)){
520send_initial_queries(
tinfo* ti,
unsigned minimal,
unsigned noaltscreen,
521 unsigned draininput){
532 total += strlen(
SMCUP);
550 ssize_t directiveb = send_initial_directives(ti->
qterm, fd);
555 loginfo(
"sent %" PRIuPTR
"B", total);
565 if(term_emit(popcolors, ttyfp,
true)){
571 logerror(
"alternate screen is unavailable");
585 if(tty_emit(smcup, fd) < 0){
601 if(term_emit(pushcolors, ttyfp,
true)){
617 logerror(
"can't leave alternate screen");
633 if(term_emit(popcolors, fp,
true)){
637 if(tty_emit(rmcup, fd)){
653 if(term_emit(popcolors, fp,
true)){
664add_u7_escape(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
665 const char* u7 = get_escape(ti,
ESCAPE_U7);
676add_smulx_escapes(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
693kill_appsync_escapes(
tinfo* ti){
699add_appsync_escapes_sm(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
711add_appsync_escapes_dcs(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
723add_pushcolors_escapes(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
735apply_kitty_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
741 if(add_smulx_escapes(ti, tablelen, tableused)){
746 if(compare_versions(ti->
termversion,
"0.20.0") >= 0){
751 if(compare_versions(ti->
termversion,
"0.23.1") > 0){
752 if(add_pushcolors_escapes(ti, tablelen, tableused)){
761 kill_appsync_escapes(ti);
767apply_alacritty_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused,
768 bool* forcesdm,
bool* invertsixel){
773 if(add_appsync_escapes_dcs(ti, tablelen, tableused)){
777 if(compare_versions(ti->
termversion,
"0.15.1") < 0){
784apply_vte_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
787 if(add_smulx_escapes(ti, tablelen, tableused)){
795apply_foot_heuristics(
tinfo* ti,
bool* forcesdm,
bool* invertsixel){
800 if(compare_versions(ti->
termversion,
"1.8.2") < 0){
807apply_gnuscreen_heuristics(
tinfo* ti){
815apply_mlterm_heuristics(
tinfo* ti){
821apply_wezterm_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
826 if(add_smulx_escapes(ti, tablelen, tableused)){
834apply_xterm_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused,
835 bool* forcesdm,
bool* invertsixel){
842 if(add_pushcolors_escapes(ti, tablelen, tableused)){
850apply_mintty_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused,
851 bool* forcesdm,
bool* invertsixel){
852 if(add_smulx_escapes(ti, tablelen, tableused)){
856 if(compare_versions(ti->
termversion,
"3.5.2") < 0){
864apply_msterminal_heuristics(
tinfo* ti){
867 return "Windows ConHost";
871apply_contour_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused,
872 bool* forcesdm,
bool* invertsixel){
873 if(add_smulx_escapes(ti, tablelen, tableused)){
876 if(add_pushcolors_escapes(ti, tablelen, tableused)){
883 *invertsixel =
false;
888apply_iterm_heuristics(
tinfo* ti,
size_t* tablelen,
size_t* tableused){
890 if(add_appsync_escapes_dcs(ti, tablelen, tableused)){
899apply_rxvt_heuristics(
tinfo* ti){
906apply_terminology_heuristics(
tinfo* ti){
909 return "Terminology";
913apply_konsole_heuristics(
tinfo* ti){
920apply_linux_heuristics(
tinfo* ti,
unsigned nonewfonts){
921 const char* tname =
NULL;
929 setup_fbcon_bitmaps(ti, ti->linux_fb_fd);
957 size_t* tablelen,
size_t* tableused,
958 bool* forcesdm,
bool* invertsixel,
959 unsigned nonewfonts){
972 const char* newname =
NULL;
975 newname = apply_kitty_heuristics(ti, tablelen, tableused);
978 newname = apply_alacritty_heuristics(ti, tablelen, tableused,
979 forcesdm, invertsixel);
982 newname = apply_vte_heuristics(ti, tablelen, tableused);
985 newname = apply_foot_heuristics(ti, forcesdm, invertsixel);
991 newname = apply_gnuscreen_heuristics(ti);
994 newname = apply_mlterm_heuristics(ti);
997 newname = apply_wezterm_heuristics(ti, tablelen, tableused);
1000 newname = apply_xterm_heuristics(ti, tablelen, tableused,
1001 forcesdm, invertsixel);
1004 newname = apply_mintty_heuristics(ti, tablelen, tableused,
1005 forcesdm, invertsixel);
1008 newname = apply_msterminal_heuristics(ti);
1011 newname = apply_contour_heuristics(ti, tablelen, tableused,
1012 forcesdm, invertsixel);
1015 newname = apply_iterm_heuristics(ti, tablelen, tableused);
1018 newname = apply_rxvt_heuristics(ti);
1021 newname =
"Terminal.app";
1024 newname = apply_linux_heuristics(ti, nonewfonts);
1027 newname = apply_terminology_heuristics(ti);
1030 newname = apply_konsole_heuristics(ti);
1036 if(newname ==
NULL){
1037 logerror(
"no name provided for termtype %d", qterm);
1042 if(wcwidth(L
'⣿') < 0){
1046 if(wcwidth(L
'🬸') < 0){
1050 if(wcwidth(L
'') < 0){
1062build_supported_styles(
tinfo* ti){
1063 const struct style {
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){
1101macos_early_matches(
void){
1102 const char* tp = getenv(
"TERM_PROGRAM");
1106 if(strcmp(tp,
"Apple_Terminal")){
1122unix_early_matches(
const char* term){
1127 if(strncmp(term,
"rxvt", 4) == 0){
1136do_terminfo_lookups(
tinfo *ti,
size_t* tablelen,
size_t* tableused){
1138 const struct strtdesc {
1175 for(typeof(*strtdescs)* strtdesc = strtdescs ; strtdesc->esc <
ESCAPE_MAX ; ++strtdesc){
1176 if(init_terminfo_esc(ti, strtdesc->tinfo, strtdesc->esc, tablelen, tableused)){
1182 logpanic(
"required terminfo capability 'cup' not defined");
1190handle_responses(
tinfo* ti,
size_t* tablelen,
size_t* tableused,
1191 int* cursor_y,
int* cursor_x,
unsigned draininput,
1192 unsigned* kitty_graphics){
1203 if(add_appsync_escapes_sm(ti, tablelen, tableused)){
1295 unsigned noaltscreen,
unsigned nocbreak,
unsigned nonewfonts,
1297 int lmargin,
int tmargin,
int rmargin,
int bmargin,
1298 unsigned draininput){
1301 const char* termtype = getenv(
"TERM");
1302 int foolcursor_x, foolcursor_y;
1304 cursor_x = &foolcursor_x;
1307 cursor_y = &foolcursor_y;
1309 *cursor_x = *cursor_y = -1;
1319 size_t tablelen = 0;
1320 size_t tableused = 0;
1321 const char* tname =
NULL;
1323 ti->
qterm = macos_early_matches();
1324#elif defined(__MINGW32__)
1326 logwarn(
"termtype (%s) ignored on windows", termtype);
1329 logpanic(
"failed opening Windows ConPTY");
1333 ti->
qterm = unix_early_matches(termtype);
1334#if defined(__linux__)
1335 ti->linux_fb_fd = -1;
1336 ti->linux_fbuffer = MAP_FAILED;
1348 logpanic(
"couldn't preserve terminal state for %d (%s)", ti->
ttyfd, strerror(errno));
1361 if(send_initial_queries(ti, minimal, noaltscreen, draininput)){
1369 if(setupterm(termtype, ti->
ttyfd, &termerr)){
1370 logpanic(
"terminfo error %d for [%s] (see terminfo(3ncurses))",
1371 termerr, termtype ? termtype :
"");
1376 int linesigs_enabled = 1;
1379 linesigs_enabled = 0;
1383 stats, draininput, linesigs_enabled)){
1387 get_default_geometry(ti);
1393 int colors = tigetnum(
"colors");
1401 if(do_terminfo_lookups(ti, &tablelen, &tableused)){
1408 if(tty_emit(tiparm(smkx), ti->
ttyfd) < 0){
1409 logpanic(
"error enabling keypad transmit mode");
1414 if(tigetflag(
"bce") > 0){
1427 if(init_terminfo_esc(ti,
"smcup",
ESCAPE_SMCUP, &tablelen, &tableused) ||
1428 init_terminfo_esc(ti,
"rmcup",
ESCAPE_RMCUP, &tablelen, &tableused)){
1436 if(strcmp(smcup,
SMCUP)){
1437 logwarn(
"warning: non-standard smcup!");
1446 if(terminfostr(&chts,
"chts") == 0){
1460 const char* op = get_escape(ti,
ESCAPE_OP);
1461 if(op && strcmp(op,
"\x1b[39;49m") == 0){
1469 if(handle_responses(ti, &tablelen, &tableused, cursor_y, cursor_x,
1484 if(init_terminfo_esc(ti,
"hpa",
ESCAPE_HPA, &tablelen, &tableused)){
1488 if(*cursor_x >= 0 && *cursor_y >= 0){
1489 if(add_u7_escape(ti, &tablelen, &tableused)){
1493 bool forcesdm =
false;
1494 bool invertsixel =
false;
1495 if(apply_term_heuristics(ti, tname, ti->
qterm, &tablelen, &tableused,
1496 &forcesdm, &invertsixel, nonewfonts)){
1499 build_supported_styles(ti);
1503 setup_sixel_bitmaps(ti, ti->
ttyfd, forcesdm, invertsixel);
1529 del_curterm(cur_term);
1536 size_t tlen = strlen(ti->
termname) + 1;
1541 char* ret = malloc(slen);
1545 ret[tlen - 1] =
' ';
1559 const char* u7 = get_escape(ti,
ESCAPE_U7);
1561 logwarn(
"no support in terminfo");
1565 logwarn(
"no valid path for cursor report");
1572 loginfo(
"got a report from %d %d/%d", fd, *cursor_y, *cursor_x);
1578 int i = ioctl(fd, TIOCGWINSZ, ws);
1580 logerror(
"TIOCGWINSZ failed on %d (%s)", fd, strerror(errno));
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);
1597 int ttyfd = ti->
ttyfd;
1602 struct termios modtermios;
1603 memcpy(&modtermios, ti->
tpreserved,
sizeof(modtermios));
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));
1618 if(!GetConsoleMode(ti->inhandle, &mode)){
1619 logerror(
"error acquiring input mode");
1622 mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
1623 if(!SetConsoleMode(ti->inhandle, mode)){
1624 logerror(
"error setting input mode");
1633 #define ENVVAR "TERM"
1634 const char* oldterm = getenv(
ENVVAR);
1636 logdebug(
"replacing %s value %s with %s",
ENVVAR, oldterm, tname);
1640 if(oldterm && strcmp(oldterm, tname) == 0){
1643 char* buf = malloc(strlen(tname) + strlen(
ENVVAR) + 1);
1647 int c = putenv(buf);
1649 logerror(
"couldn't export %s", buf);
void set_pixel_blitter(ncblitter blitfxn)
int get_tty_fd(FILE *ttyfp)
int init_inputlayer(tinfo *ti, FILE *infp, int lmargin, int tmargin, int rmargin, int bmargin, ncsharedstats *stats, unsigned drain, int linesigs_enabled)
struct initial_responses * inputlayer_get_responses(inputctx *ictx)
int get_cursor_location(inputctx *ictx, const char *u7, unsigned *y, unsigned *x)
int stop_inputlayer(tinfo *ti)
int kitty_remove(int id, fbuf *f)
int kitty_move(sprixel *s, fbuf *f, unsigned noscroll, int yoff, int xoff)
int kitty_wipe(sprixel *s, int ycell, int xcell)
int kitty_scrub(const ncpile *p, sprixel *s)
int kitty_blit_selfref(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
int kitty_rebuild_selfref(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
int kitty_clear_all(fbuf *f)
int kitty_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
int kitty_rebuild_animation(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
int kitty_blit(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
int kitty_draw(const tinfo *ti, const ncpile *p, sprixel *s, fbuf *f, int yoff, int xoff)
uint8_t * kitty_trans_auxvec(const ncpile *p)
int kitty_wipe_animation(sprixel *s, int ycell, int xcell)
int kitty_commit(fbuf *f, sprixel *s, unsigned noscroll)
int kitty_wipe_selfref(sprixel *s, int ycell, int xcell)
int kitty_blit_animated(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
int fbcon_scrub(const struct ncpile *p, sprixel *s)
int fbcon_draw(const tinfo *ti, sprixel *s, int y, int x)
int fbcon_blit(struct ncplane *n, int linesize, const void *data, int leny, int lenx, const struct blitterargs *bargs)
void fbcon_scroll(const struct ncpile *p, tinfo *ti, int rows)
int fbcon_wipe(sprixel *s, int ycell, int xcell)
int fbcon_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
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,...)
#define logdebug(fmt,...)
#define logpanic(fmt,...)
#define NCSTYLE_UNDERCURL
#define NCSTYLE_UNDERLINE
API int API int const nccell unsigned len
uint8_t * sixel_trans_auxvec(const ncpile *p)
int sixel_scrub(const ncpile *p, sprixel *s)
void sixel_refresh(const ncpile *p, sprixel *s)
int sixel_init(tinfo *ti, int fd)
int sixel_init_inverted(tinfo *ti, int fd)
int sixel_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
void sixel_cleanup(tinfo *ti)
int sixel_draw(const tinfo *ti, const ncpile *p, sprixel *s, fbuf *f, int yoff, int xoff)
int sixel_wipe(sprixel *s, int ycell, int xcell)
int sixel_blit(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
int sixel_init_forcesdm(tinfo *ti, int fd)
int sprite_init(tinfo *t, int fd)
#define SIXEL_MAX_REGISTERS
queried_terminals_e qterm
unsigned appsync_supported
uint32_t chans[NCPALETTESIZE]
queried_terminals_e qterm
unsigned supported_styles
ncpixelimpl_e pixel_implementation
int(* pixel_init)(struct tinfo *ti, int fd)
int(* pixel_draw_late)(const struct tinfo *, struct sprixel *s, int yoff, int xoff)
int(* pixel_remove)(int id, fbuf *f)
int(* pixel_rebuild)(struct sprixel *s, int y, int x, uint8_t *auxvec)
uint32_t bg_collides_default
int(* pixel_wipe)(struct sprixel *s, int y, int x)
void(* pixel_cleanup)(struct tinfo *)
int(* pixel_commit)(fbuf *f, struct sprixel *s, unsigned noscroll)
int(* pixel_scrub)(const struct ncpile *p, struct sprixel *s)
ncpalette originalpalette
unsigned sprixel_scale_height
struct termios * tpreserved
void(* pixel_refresh)(const struct ncpile *p, struct sprixel *s)
int(* pixel_draw)(const struct tinfo *, const struct ncpile *p, struct sprixel *s, fbuf *f, int y, int x)
uint8_t *(* pixel_trans_auxvec)(const struct ncpile *p)
int(* pixel_clear_all)(fbuf *f)
unsigned sixel_maxy_pristine
int(* pixel_move)(struct sprixel *s, fbuf *f, unsigned noscroll, int yoff, int xoff)
void(* pixel_scroll)(const struct ncpile *p, struct tinfo *, int rows)
uint16_t escindices[ESCAPE_MAX]
int enter_alternate_screen(int fd, FILE *ttyfp, tinfo *ti, unsigned drain)
int grow_esc_table(tinfo *ti, const char *tstr, escape_e esc, size_t *tlen, size_t *tused)
int putenv_term(const char *tname)
int locate_cursor(tinfo *ti, unsigned *cursor_y, unsigned *cursor_x)
void free_terminfo_cache(tinfo *ti)
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)
int cbreak_mode(tinfo *ti)
int leave_alternate_screen(int fd, FILE *fp, tinfo *ti, unsigned drain)
char * termdesc_longterm(const tinfo *ti)
int tiocgwinsz(int fd, struct winsize *ws)
int prepare_windows_terminal(struct tinfo *ti, size_t *tablelen, size_t *tableused)