Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: query terminal properties on ZLE startup
- X-seq: zsh-workers 53372
- From: Oliver Kiddle <opk@xxxxxxx>
- To: Zsh workers <zsh-workers@xxxxxxx>
- Subject: PATCH: query terminal properties on ZLE startup
- Date: Fri, 21 Feb 2025 02:05:42 +0100
- Archived-at: <https://zsh.org/workers/53372>
- List-id: <zsh-workers.zsh.org>
For some time, I've included lines in my .zshrc to send terminal escape
sequences to detect the terminal background colour so I can avoid
problems with unreadable colour combinations. I've found this to be
useful but it's somewhat tricky to get right using only shell code.
There are other things that can be queried from the terminal and which
may be useful. It's not uncommon for modern terminal-based programs to
query these, including some shells. Admittedly most of them have the
advantage of clearing the whole screen to quickly hide any unwanted
effects that might appear as a result. I'm aware of the disadvantages of
these terminal queries - it's a pity this can't be done out-of-band like
the TIOCGWINSZ ioctl.
The only thing this does so far is populate variables. So as an example:
% typeset -m .term.\*
.term.bg='#ffffdd'
.term.extensions=( truecolor modkeys-kitty )
.term.fg='#000000'
.term.id=foot
.term.version=1.20.2
.term.mode=light
I chose a .term namespace because these are properties of the terminal
rather than of ZLE. .term.extensions needs most explanation. Each value
in the array corresponds to a feature where zsh uses hard-coded escapes.
Particular extensions can be enabled/disabled by putting values,
optionally prefixed by `-', in .term.extensions. Aside from disabling
queries, it might in future provide a way to handle other features that
rely on extensions - currently we do 24-bit color, bracketed paste and
clipboard copying. I'd like to avoid getting into using heuristics based
on id and version but this at least puts that option into the user's
hands.
I've done fairly extensive testing with different terminals and the
worst regression I've found is terminology which clears the screen. A
few output some text that gets quicky wiped. Unfortunately, this loses
the effect of PROMPT_SP on the initial run of the shell for the first
prompt. Putting the queries after that sequence is printed won't set the
variables until .zshrc is finished.
When reading responses, it takes care to unget any characters that
weren't consumed. This does mean that if you managed to type, not only a
command but the initial part of your input to the command before zle is
initialised then zsh will have grabbed all that input. I guess we could
try ungetc(). Type-ahead does get run through the sequence parser. We
could use a FIONREAD ioctl first to minimise that but it didn't seem
worth the bother.
There's a 1/2 second timeout on the reads so it won't get stuck
waiting for responses. It does generate a device attributes (DA)
query last which is supported by nearly everything and the response from
that should short-circuit the delay. I've tested over slower links such
as a 9600 serial link. 0.2s reliably works at that speed.
The approach taken for parsing escape sequences is perhaps somewhat
overkill. As it's fairly heavy on macros, this has gone in a new source
file.
Oliver
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 69298855f..dd5a735b4 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -963,6 +963,40 @@ there is a block of reserved or unused signal numbers before the POSIX
real-time signals so the array index can't be used as an accurate indicator
of their signal number. Use, for example, tt(kill -l SIGRTMIN) instead.
)
+vindex(.term.bg)
+item(tt(.term.bg))(
+The background color of the terminal if the terminal supports the
+necessary interface for retrieving this information.
+
+See also the tt(.term.extensions) parameter.
+)
+vindex(.term.fg)
+item(tt(.term.fg))(
+Like tt(.term.bg) but for the foreground color.
+)
+vindex(.term.id)
+item(tt(.term.id))(
+Like the tt(TERM) parameter, this identifies the terminal but is obtained by
+querying the terminal. Many terminals name a different common terminal in
+tt(TERM) that they then emulate, often imperfectly. This allows them to work
+without the need to distribute updates to terminal capability databases. The
+more accurate identification in this parameter may be useful when configuring
+aspects of the shell differently for specific terminals.
+
+See also the tt(.term.extensions) parameter.
+)
+vindex(.term.version)
+item(tt(.term.version))(
+The version number of the terminal application, set in conjunction with
+tt(.term.id).
+)
+vindex(.term.mode)
+item(tt(.term.mode))(
+Either tt(light) or tt(dark). In cases where the terminal background color is
+detected and set in tt(.term.bg), this is set to provide a basic indication of
+how light that color is. This may help with configuring readable color
+combinations.
+)
vindex(TRY_BLOCK_ERROR)
item(tt(TRY_BLOCK_ERROR) <S>)(
In an tt(always) block, indicates whether the preceding list of code
@@ -1700,6 +1734,43 @@ causes the shell to reinitialise the terminal, making the workaround
`tt(TERM=$TERM)' unnecessary. Note that unlike other colon-separated
arrays this is not tied to a zsh array.
)
+vindex(.term.extensions)
+item(tt(.term.extensions))(
+An array containing a list of extension features of the terminal to be enabled
+or disabled (prefixed with `tt(-)'). When ZLE starts, it will add entries for
+features that were auto-detected. This auto-detection uses extensions itself,
+all named with a `tt(query)' prefix. As these are used when ZLE starts they
+would need to be disabled early in the startup files if they are to be
+disabled. A value of `tt(-query)' will disable all terminal queries on
+startup. Extensions can be any of the following, where the marks `<D>' and
+`<E>' indicate whether they are disabled or enabled by default:
+
+startitem()
+item(tt(query-bg) <E>)(
+Query the terminal background color which is used for tt(.term.bg) and
+tt(.term.mode).
+)
+item(tt(query-fg) <E>)(
+Query the terminal foreground color which is used for tt(.term.fg).
+)
+item(tt(query-id) <E>)(
+Query the terminal identification which is used for tt(.term.id) and
+tt(.term.version).
+)
+xitem(tt(modkeys-kitty) <D>)
+item(tt(query-modkeys-kitty) <E>)(
+Support for the kitty keyboard handling protocol which enables reporting of a
+wider range of key combinations and resolves problems with ambiguous key
+sequences. Currently there is only support for detecting whether the terminal
+supports this feature.
+)
+xitem(tt(truecolor) <E>)
+item(tt(query-truecolor) <E>)(
+Support for 24-bit truecolor escape sequences. Auto-detection also tries
+termcap and the tt(COLORTERM) environment variable.
+)
+enditem()
+)
vindex(TIMEFMT)
item(tt(TIMEFMT))(
The format of process time reports with the tt(time) keyword.
diff --git a/Src/Zle/termquery.c b/Src/Zle/termquery.c
new file mode 100644
index 000000000..ccd6eaef0
--- /dev/null
+++ b/Src/Zle/termquery.c
@@ -0,0 +1,510 @@
+/*
+ * termquery.c - terminal feature probes
+ *
+ * This file is part of zsh, the Z shell.
+ *
+ * Copyright (c) 2025 Oliver Kiddle
+ * All rights reserved.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and to distribute modified versions of this software for any
+ * purpose, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * In no event shall Oliver Kiddle or the Zsh Development Group be liable
+ * to any party for direct, indirect, special, incidental, or consequential
+ * damages arising out of the use of this software and its documentation,
+ * even if Oliver Kiddle and the Zsh Development Group have been advised of
+ * the possibility of such damage.
+ *
+ * Oliver Kiddle and the Zsh Development Group specifically disclaim any
+ * warranties, including, but not limited to, the implied warranties of
+ * merchantability and fitness for a particular purpose. The software
+ * provided hereunder is on an "as is" basis, and Oliver Kiddle and the
+ * Zsh Development Group have no obligation to provide maintenance,
+ * support, updates, enhancements, or modifications.
+ *
+ */
+
+#include "zle.mdh"
+#include "termquery.pro"
+
+/* On a 9600 serial link, 0.2 seconds is about the minimum to safely work.
+ * 0.5s should be generous enough. We only incur this delay on a terminal
+ * that doesn't respond to device attributes requests. */
+#define TIMEOUT -51L
+
+#define TAG (1 << 7)
+#define SEQ (TAG | (1 << 6))
+
+/* tags */
+#define T_BEGIN 0x80 /* group start */
+#define T_END 0x81 /* group end */
+#define T_OR 0x82 /* alternation (within group) */
+#define T_REPEAT 0x83 /* repeat preceding block */
+#define T_NUM 0x84 /* decimal number, defaults to 0 if absent */
+#define T_HEX 0x85 /* hexadecimal digit used as part of a number */
+#define T_HEXCH 0x86 /* hexadecimal digit that is thrown away */
+#define T_WILDCARD 0x87 /* match any character */
+#define T_RECORD 0x88 /* start text capture */
+#define T_CAPTURE 0x89 /* end text capture */
+#define T_DROP 0x91 /* drop input + restart without matching a sequence */
+#define T_CONTINUE 0x92 /* when matching don't go back to first state */
+#define T_NEXT 0x94 /* advance to next stored number */
+
+typedef const unsigned char seqstate_t;
+
+/* macros for entries in the parse state table */
+#define OR "\x82"
+#define NUM "\x84"
+#define HEX "\x85"
+#define HEXCH "\x86"
+#define WILDCARD "\x87"
+#define RECORD "\x88"
+#define CAPTURE "\x89"
+#define EITHER(group) "\x80" group "\x81"
+#define OPT(opt) EITHER( opt OR EITHER() )
+#define REPEAT(r) EITHER( r ) "\x83" /* would use OPT() but done in C code */
+/* Sequence completion tags and other actions need to precede the state
+ * where the final character is matched. This macro just reverses the
+ * order so that they come in a more logical order. */
+#define MATCH(ch, arg) arg ch
+#define DROP "\x91"
+#define CONTINUE "\x92"
+#define NEXT "\x94"
+#define DA "\xc0"
+#define COLOR "\xc1"
+#define KITTY "\xc2"
+#define TINFO "\xc3"
+#define XTID "\xc4"
+#define XTVER "\xc5"
+
+/* Deterministic finite state automata for parsing terminal response sequences.
+ * - alternatives decided by the first character (no back-tracking)
+ * - matching literal characters is 7-bit ASCII only
+ */
+#define QUERY_STATES \
+ "\033" \
+ EITHER( /* default terminal colours */ \
+ "]1" NUM ";" \
+ EITHER( "rgb:" \
+ HEX OPT( HEX ) OPT( HEXCH HEXCH ) "/" \
+ HEX OPT( HEX ) OPT( HEXCH HEXCH ) "/" \
+ HEX OPT( HEX ) OPT( HEXCH HEXCH ) \
+ OR "#" \
+ HEX HEX NEXT HEX HEX NEXT HEX HEX ) \
+ EITHER( \
+ MATCH("\033", COLOR CONTINUE ) \
+ MATCH("\\", DROP) /* urxvt 9.31 has bug: it omits the backslash */ \
+ OR MATCH("\007", COLOR ) ) \
+ OR "P" /* DCS */ \
+ EITHER( /* terminal name and version */ \
+ ">|" RECORD \
+ REPEAT( \
+ CAPTURE EITHER( MATCH("(", XTID CONTINUE) \
+ OR MATCH(" ", XTID CONTINUE) ) RECORD \
+ OR \
+ CAPTURE OPT( ")" ) "\033" MATCH( "\\", XTVER ) \
+ OR \
+ WILDCARD \
+ ) \
+ OR /* 24-bit colour support */ \
+ EITHER( \
+ "0+r" OPT( "524742" ) /* mlterm responds without numbers */ \
+ "\033" MATCH("\\", DROP) /* kitty does 24-bit but 0 => no */ \
+ OR "1+r524742=" REPEAT( HEXCH HEXCH ) /* hex encoded bytes */ \
+ "\033" MATCH("\\", TINFO) )) /* any value => truecolor */ \
+ OR /* keyboard protocol and device attributes */ \
+ "[?" \
+ REPEAT( NUM \
+ EITHER( ";" \
+ OR MATCH("u", KITTY ) \
+ OR MATCH("c", DA) )))
+
+static char *EXTVAR = ".term.extensions";
+static char *IDVAR = ".term.id";
+static char *VERVAR = ".term.version";
+static char *BGVAR = ".term.bg";
+static char *FGVAR = ".term.fg";
+static char *MODEVAR = ".term.mode";
+
+/* Query sequences
+ * using ESC\\ as ST instead of BEL because the bell was emitted on
+ * old xterm. */
+
+/* Terminal default colors. Probably best that these are queried first
+ * because tmux will need to pass these on. */
+#define TQ_BGCOLOR "\033]11;?\033\\"
+#define TQ_FGCOLOR "\033]10;?\033\\"
+
+/* Kitty / fixterms keyboard protocol which allows wider support for keys
+ * and modifiers. This clears the screen in terminology. */
+#define TQ_KITTYKB "\033[?u"
+
+/* Query for 24-bit color support, This is an XTTERMCAP sequence which with
+ * some terminals allows terminfo entries to be retrieved. */
+#define TQ_RGB "\033P+q524742\033\\"
+
+/* Query terminal name and version which is useful with terminals that
+ * lie in $TERM so that they work without terminfo entries. */
+#define TQ_XTVERSION "\033[>0q"
+
+/* Device attributes, Response isn't especially interesting but we get a
+ * response from most terminals so by putting this last we can short-circuit
+ * the time delay waiting for less well-supported responses that might never
+ * come.
+ * Following two spaces + CR works to clear fragments of sequences that
+ * appeared. GNU screen is an example terminal that needs this. */
+#define TQ_DA "\033[c \r"
+
+static seqstate_t*
+find_branch(seqstate_t* pos)
+{
+ int nested = 0;
+ seqstate_t* cur = pos + 1;
+
+ for (; *cur && (nested || (*cur != T_END && *cur != T_OR)); cur++) {
+ if (*cur == T_BEGIN)
+ nested++;
+ else if (*cur == T_END)
+ nested--;
+ }
+ return cur;
+}
+
+static seqstate_t*
+find_matching(seqstate_t* pos, int direction)
+{
+ int nested = 1;
+ seqstate_t* cur = pos + direction;
+
+ for (; *cur && nested; cur += direction) {
+ if (*cur == T_BEGIN && !(nested += direction)) {
+ break; /* going backward, stop on begin */
+ } else if (*cur == T_END)
+ nested -= direction;
+ }
+ return cur;
+}
+
+static void
+probe_terminal(const char *tquery, seqstate_t *states,
+ void (*handle_seq) (int seq, int *numbers, int len,
+ char *capture, int clen))
+{
+ size_t blen = 256, nlen = 16;
+ char *buf = zhalloc(blen);
+ char *start = buf, *current = buf, *illgotten = buf;
+ size_t record = 0, capture = 0;
+ int *numbers = hcalloc(nlen * sizeof(int));
+ int *num = numbers;
+ int finish = 0, number = 0;
+ int ch;
+ struct ttyinfo ti;
+
+ seqstate_t *curstate = states;
+
+ gettyinfo(&ti);
+#ifdef HAS_TIO
+ ti.tio.c_lflag &= (~ECHO & ~ICANON & ~ISIG);
+ ti.tio.c_iflag &= ~ICRNL;
+#else
+ ti.sgttyb.sg_flags &= ~ECHO;
+ ti.sgttyb.sg_flags |= CBREAK;
+#endif
+ settyinfo(&ti);
+
+ fputs(tquery, shout);
+ fflush(shout);
+
+ while (!finish && *curstate) {
+ int consumed = 0; /* whether an input token has been matched */
+ int branches = 1; /* count of untried paths encountered */
+ int triedstart = curstate == states; /* current char tried at start */
+ unsigned char action = 0, sequence = 0;
+
+ if (illgotten < current) {
+ ch = *illgotten++;
+ } else {
+ if (current == buf + blen) {
+ current = hrealloc(buf, blen, blen * 2);
+ illgotten = current + (illgotten - buf);
+ start = current + (start - buf);
+ buf = current;
+ current = buf + blen;
+ memset(current, 0, blen);
+ blen *= 2;
+ }
+ if ((ch = getbyte(TIMEOUT, 0, 1)) == EOF)
+ break;
+ *current++ = ch;
+ illgotten = current;
+ }
+
+ while (!consumed && branches >= 1 && *curstate) {
+ int increment = 0, base = 1, tryhere = 0;
+
+ do {
+ switch (*curstate) {
+ case T_BEGIN:
+ branches++;
+ curstate++;
+ break;
+ case T_END:
+ branches--;
+ sequence = action = 0;
+ curstate++;
+ break;
+ case T_OR:
+ curstate = find_matching(curstate, 1);
+ break;
+ case T_REPEAT:
+ sequence = action = 0;
+ if (branches > 1) {
+ branches--;
+ curstate++;
+ } else {
+ branches++;
+ curstate = find_matching(curstate - 1, -1);
+ }
+ break;
+ case T_NUM:
+ if (!(tryhere = (ch >= '0' && ch <= '9')))
+ curstate++;
+ break;
+ case T_RECORD:
+ record = current - buf - 1;
+ curstate++;
+ break;
+ case T_CAPTURE:
+ capture = current - buf - 1;
+ curstate++;
+ break;
+ case T_DROP:
+ case T_CONTINUE:
+ case T_NEXT:
+ action |= *curstate;
+ curstate++;
+ break;
+ default:
+ if ((*curstate & SEQ) == SEQ) {
+ sequence = *curstate;
+ curstate++;
+ } else {
+ tryhere = 1;
+ }
+ }
+ } while (!tryhere);
+
+ switch (*curstate) {
+ case T_HEX:
+ if (ch >= '0' && ch <= '9') {
+ increment = ch - '0';
+ } else if (ch >= 'a' && ch <= 'f') {
+ increment = ch - 'a' + 10;
+ } else if (ch >= 'A' && ch <= 'F') {
+ increment = ch - 'A' + 10;
+ } else
+ break;
+ consumed = number = 1;
+ base = 16;
+ if (action & 4) /* NEXT was used */
+ ++num;
+ break;
+ case T_HEXCH:
+ consumed = (ch >= '0' && ch <= '9') ||
+ (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F');
+ if (consumed && number) {
+ ++num;
+ number = 0;
+ }
+ break;
+ case T_NUM:
+ if (ch >= '0' && ch <= '9') {
+ increment = ch - '0';
+ base = 10;
+ consumed = number = 1;
+ curstate--; /* allow repetition */
+ }
+ break;
+ case T_WILDCARD:
+ consumed = 1;
+ if (number) {
+ ++num;
+ number = 0;
+ }
+ break;
+ default:
+ if (!(*curstate & TAG) && (consumed = (*curstate == ch)) &&
+ number)
+ {
+ ++num;
+ number = 0;
+ }
+ break;
+ }
+ if (num == numbers + nlen) {
+ numbers = hrealloc((char *) numbers, nlen * sizeof(int),
+ sizeof(int) * nlen * 2);
+ memset(numbers + nlen, 0, nlen * sizeof(int));
+ num = numbers + nlen; /* restore relative position */;
+ nlen *= 2;
+ }
+ if (number)
+ *num = *num * base + increment;
+
+ /* if it didn't match, move to the next OR */
+ if (!consumed && branches > 1) {
+ sequence = action = 0;
+ for (; branches > 1; branches--) {
+ curstate = find_branch(curstate);
+ if (*curstate == T_OR) {
+ curstate++;
+ break;
+ /* repeated group can match zero times */
+ } else if (curstate[1] == T_REPEAT)
+ break;
+ }
+ }
+ /* Retry character at the start if it is the only buffered
+ * character and was tried from a later state. */
+ if (!consumed && branches <= 1) {
+ if (triedstart || start + 1 != current)
+ break;
+ branches = 1;
+ curstate = states;
+ triedstart = 1;
+ memset((num = numbers), 0, nlen * sizeof(int));
+ sequence = action = number = 0;
+ }
+ }
+
+ if (!consumed) {
+ illgotten = ++start;
+ curstate = states; /* return to first state */
+ memset((num = numbers), 0, nlen * sizeof(int));
+ number = 0;
+ } else {
+ if (sequence && !(finish = sequence == SEQ))
+ handle_seq(sequence & ~SEQ, numbers, num - numbers,
+ buf + record, capture - record);
+
+ if ((sequence || (action & 1)) &&
+ (current = start) && /* drop input from sequence */
+ (!(action & 2)))
+ {
+ curstate = states;
+ memset((num = numbers), 0, nlen * sizeof(int));
+ number = 0;
+ } else { /* CONTINUE */
+ while (*++curstate == T_END)
+ ;
+ }
+ }
+ }
+
+ /* put back any type-ahead text */
+ if (current > buf)
+ ungetbytes(buf, current - buf);
+
+ settyinfo(&shttyinfo);
+}
+
+static void
+handle_color(int bg, int red, int green, int blue)
+{
+ char *colour;
+
+ switch (bg) {
+ case 1: /* background color */
+ /* scale by Rec.709 coefficients for lightness */
+ setsparam(MODEVAR, ztrdup(
+ 0.2126f * red + 0.7152f * green + 0.0722f * blue <= 127 ?
+ "dark" : "light"));
+ /* fall-through */
+ case 0:
+ colour = zalloc(8);
+ sprintf(colour, "#%02x%02x%02x", red, green, blue);
+ setsparam(bg ? BGVAR : FGVAR, colour);
+ break;
+ default: break;
+ }
+}
+
+/* roughly corresponding feature names */
+static const char *features[] =
+ { "bg", "fg", "modkeys-kitty", "truecolor", "id" };
+static const char *queries[] =
+ { TQ_BGCOLOR, TQ_FGCOLOR, TQ_KITTYKB, TQ_RGB, TQ_XTVERSION, TQ_DA };
+
+static void
+handle_query(int sequence, int *numbers, int len, char *capture, int clen)
+{
+ char **feat;
+
+ switch (sequence) {
+ case 1: /* default colour */
+ if (len == 4)
+ handle_color(numbers[0], numbers[1], numbers[2], numbers[3]);
+ break;
+ case 2: /* kitty keyboard */
+ feat = zshcalloc(2 * sizeof(char *));
+ *feat = ztrdup(features[2]);
+ assignaparam(EXTVAR, feat, ASSPM_WARN|ASSPM_AUGMENT);
+ break;
+ case 3: /* truecolor */
+ feat = zshcalloc(2 * sizeof(char *));
+ *feat = ztrdup(features[3]);
+ assignaparam(EXTVAR, feat, ASSPM_WARN|ASSPM_AUGMENT);
+ break;
+ case 4: /* id */
+ setsparam(IDVAR, ztrduppfx(capture, clen));
+ break;
+ case 5: /* version */
+ setsparam(VERVAR, ztrduppfx(capture, clen));
+ break;
+ }
+}
+
+/**/
+void
+query_terminal(void) {
+ char tquery[sizeof(TQ_BGCOLOR TQ_FGCOLOR TQ_KITTYKB TQ_RGB TQ_XTVERSION TQ_DA)];
+ char *tqend = tquery;
+ static seqstate_t states[] = QUERY_STATES;
+ char **f, **flist = getaparam(EXTVAR);
+ int i;
+
+ for (f = flist; f && *f; f++)
+ if (!strcmp(*f, "-query"))
+ return; /* disable all queries */
+
+ for (i=0; i < sizeof(queries)/sizeof(*queries); i++) {
+ int last = i >= sizeof(features)/sizeof(*features);
+ int found = (last && tqend == tquery);
+ char *cterm;
+
+ /* skip if the query or corresponding feature is already in the list */
+ for (f = flist; !last && !found && f && *f; f++)
+ found = !strcmp(*f + (**f == '-'), features[i]) ||
+ (!strncmp(*f, "-query-", 7) && !strcmp(*f + 7, features[i]));
+ if (found)
+ continue;
+ /* if termcap indicates 24-bit color, assume support - even
+ * though this is only based on the initial $TERM
+ * failing that, check $COLORTERM */
+ if (i == 3 && (tccolours == 1 << 24 ||
+ ((cterm = getsparam("COLORTERM")) &&
+ (!strcmp(cterm, "truecolor") ||
+ !strcmp(cterm, "24bit")))))
+ handle_query(3, 0, 0, 0, 0);
+ else
+ struncpy(&tqend, (char *) queries[i], /* collate escape sequences */
+ sizeof(tquery) - (tqend - tquery));
+ }
+
+ if (tqend != tquery) /* unless nothing left after filtering */
+ probe_terminal(tquery, states, &handle_query);
+}
diff --git a/Src/Zle/zle.mdd b/Src/Zle/zle.mdd
index dd69eff2c..c7c914712 100644
--- a/Src/Zle/zle.mdd
+++ b/Src/Zle/zle.mdd
@@ -8,7 +8,7 @@ autofeatures="b:bindkey b:vared b:zle"
objects="zle_bindings.o zle_hist.o zle_keymap.o zle_main.o \
zle_misc.o zle_move.o zle_params.o zle_refresh.o \
zle_thingy.o zle_tricky.o zle_utils.o zle_vi.o zle_word.o \
-textobjects.o"
+termquery.o textobjects.o"
headers="zle.h zle_things.h"
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 1afb1bf58..c89659c0b 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -2240,6 +2240,9 @@ setup_(UNUSED(Module m))
comprecursive = 0;
rdstrs = NULL;
+ /* detect terminal color and features */
+ query_terminal();
+
/* initialise the keymap system */
init_keymaps();
Messages sorted by:
Reverse Date,
Date,
Thread,
Author