From 50597692e021ee071b971e13a14dde5b22d31639 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 5 Nov 2018 22:24:10 +0100 Subject: 43759: add support for true colour terminals --- Src/Modules/nearcolor.c | 8 ++-- Src/Zle/complist.c | 9 +++-- Src/Zle/zle.h | 4 +- Src/Zle/zle_refresh.c | 31 ++++++++------- Src/Zle/zle_tricky.c | 9 +++-- Src/prompt.c | 104 ++++++++++++++++++++++++++++-------------------- Src/zsh.h | 33 ++++++++++----- Src/zsh_system.h | 4 ++ 8 files changed, 122 insertions(+), 80 deletions(-) (limited to 'Src') diff --git a/Src/Modules/nearcolor.c b/Src/Modules/nearcolor.c index 2a763d470..128658e20 100644 --- a/Src/Modules/nearcolor.c +++ b/Src/Modules/nearcolor.c @@ -118,10 +118,12 @@ static int getnearestcolor(UNUSED(Hookdef dummy), Color_rgb col) { if (tccolours == 256) - return mapRGBto256(col->red, col->green, col->blue); + return mapRGBto256(col->red, col->green, col->blue) + 1; if (tccolours == 88) - return mapRGBto88(col->red, col->green, col->blue); - return 0; + return mapRGBto88(col->red, col->green, col->blue) + 1; + /* returning 1 indicates black rather than failure (0) so this + * module still serves to prevent fallback on true color */ + return 1; } static struct features module_features = { diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index e768aee5d..429c8159f 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -1096,6 +1096,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) p += len; if (*p) { int arg = 0, is_fg; + zattr atr; if (idigit(*p)) arg = zstrtol(p, &p, 10); @@ -1159,13 +1160,13 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) /* colours must be ASCII */ if (*p == '{') { p++; - arg = match_colour((const char **)&p, is_fg, 0); + atr = match_colour((const char **)&p, is_fg, 0); if (*p == '}') p++; } else - arg = match_colour(NULL, is_fg, arg); - if (arg >= 0 && dopr) - set_colour_attribute(arg, is_fg ? COL_SEQ_FG : + atr = match_colour(NULL, is_fg, arg); + if (atr != TXT_ERROR && dopr) + set_colour_attribute(atr, is_fg ? COL_SEQ_FG : COL_SEQ_BG, 0); break; case ZWC('f'): diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 8261da92b..f06c56483 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -430,7 +430,7 @@ enum { */ struct region_highlight { /* Attributes turned on in the region */ - int atr; + zattr atr; /* Start of the region */ int start; /* Start of the region in metafied ZLE line */ @@ -488,7 +488,7 @@ typedef struct { * need the effect; 'off' attributes are only present for the * last character in the sequence. */ - int atr; + zattr atr; } REFRESH_ELEMENT; /* A string of screen cells */ diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index d0dd1ef06..1f293845f 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -149,7 +149,7 @@ char *lpromptbuf, *rpromptbuf; /* Text attributes after displaying prompts */ /**/ -unsigned pmpt_attr, rpmpt_attr; +zattr pmpt_attr, rpmpt_attr; /* number of lines displayed */ @@ -208,7 +208,7 @@ int predisplaylen, postdisplaylen; * displayed on screen. */ -static int default_atr_on, special_atr_on; +static zattr default_atr_on, special_atr_on; /* * Array of region highlights, no special termination. @@ -521,7 +521,7 @@ unset_region_highlight(Param pm, int exp) /* The last attributes that were on. */ -static int lastatr; +static zattr lastatr; /* * Clear the last attributes that we set: used when we're going @@ -560,7 +560,7 @@ tcoutclear(int cap) /**/ void -zwcputc(const REFRESH_ELEMENT *c, int *curatrp) +zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp) { /* * Safety: turn attributes off if last heard of turned on. @@ -638,7 +638,7 @@ static int zwcwrite(const REFRESH_STRING s, size_t i) { size_t j; - int curatr = 0; + zattr curatr = 0; for (j = 0; j < i; j++) zwcputc(s + j, &curatr); @@ -891,7 +891,7 @@ snextline(Rparams rpms) /**/ static void -settextattributes(int atr) +settextattributes(zattr atr) { if (txtchangeisset(atr, TXTNOBOLDFACE)) tsetcap(TCALLATTRSOFF, 0); @@ -992,7 +992,7 @@ zrefresh(void) int tmppos; /* t - tmpline */ int tmpalloced; /* flag to free tmpline when finished */ int remetafy; /* flag that zle line is metafied */ - int txtchange; /* attributes set after prompts */ + zattr txtchange; /* attributes set after prompts */ int rprompt_off = 1; /* Offset of rprompt from right of screen */ struct rparams rpms; #ifdef MULTIBYTE_SUPPORT @@ -1212,8 +1212,9 @@ zrefresh(void) rpms.s = nbuf[rpms.ln = 0] + lpromptw; rpms.sen = *nbuf + winw; for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) { - int base_atr_on = default_atr_on, base_atr_off = 0, ireg; - int all_atr_on, all_atr_off; + unsigned ireg; + zattr base_atr_on = default_atr_on, base_atr_off = 0; + zattr all_atr_on, all_atr_off; struct region_highlight *rhp; /* * Calculate attribute based on region. @@ -1446,7 +1447,8 @@ zrefresh(void) more_end = 1; if (statusline) { - int outll, outsz, all_atr_on, all_atr_off; + int outll, outsz; + zattr all_atr_on, all_atr_off; char *statusdup = ztrdup(statusline); ZLE_STRING_T outputline = stringaszleline(statusdup, 0, &outll, &outsz, NULL); @@ -1672,7 +1674,7 @@ zrefresh(void) /* output the right-prompt if appropriate */ if (put_rpmpt && !iln && !oput_rpmpt) { - int attrchange; + zattr attrchange; moveto(0, winw - rprompt_off - rpromptw); zputs(rpromptbuf, shout); @@ -1926,7 +1928,7 @@ refreshline(int ln) /* 3: main display loop - write out the buffer using whatever tricks we can */ for (;;) { - int now_off; + zattr now_off; #ifdef MULTIBYTE_SUPPORT if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) { @@ -2506,8 +2508,9 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) *vp = zr_zr; for (t0 = 0; t0 < tmpll; t0++) { - int base_atr_on = 0, base_atr_off = 0, ireg; - int all_atr_on, all_atr_off; + unsigned ireg; + zattr base_atr_on = 0, base_atr_off = 0; + zattr all_atr_on, all_atr_off; struct region_highlight *rhp; /* * Calculate attribute based on region. diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 22c381237..2b25d6b2e 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -2431,6 +2431,7 @@ printfmt(char *fmt, int n, int dopr, int doesc) /* Handle the `%' stuff (%% == %, %n == ). */ if (doesc && *p == '%') { int arg = 0, is_fg; + zattr atr; if (idigit(*++p)) arg = zstrtol(p, &p, 10); if (*p) { @@ -2482,13 +2483,13 @@ printfmt(char *fmt, int n, int dopr, int doesc) is_fg = (*p == 'F'); if (p[1] == '{') { p += 2; - arg = match_colour((const char **)&p, is_fg, 0); + atr = match_colour((const char **)&p, is_fg, 0); if (*p != '}') p--; } else - arg = match_colour(NULL, is_fg, arg); - if (arg >= 0) - set_colour_attribute(arg, is_fg ? COL_SEQ_FG : + atr = match_colour(NULL, is_fg, arg); + if (atr != TXT_ERROR) + set_colour_attribute(atr, is_fg ? COL_SEQ_FG : COL_SEQ_BG, 0); break; case 'f': diff --git a/Src/prompt.c b/Src/prompt.c index 39edbdb2b..284c02475 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -33,7 +33,7 @@ /* text attribute mask */ /**/ -mod_export unsigned txtattrmask; +mod_export zattr txtattrmask; /* the command stack for use with %_ in prompts */ @@ -168,7 +168,7 @@ promptpath(char *p, int npath, int tilde) /**/ mod_export char * -promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) +promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) { struct buf_vars new_vars; @@ -236,8 +236,8 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) } /* Parse the argument for %F and %K */ -static int -parsecolorchar(int arg, int is_fg) +static zattr +parsecolorchar(zattr arg, int is_fg) { if (bv->fm[1] == '{') { char *ep; @@ -268,10 +268,11 @@ parsecolorchar(int arg, int is_fg) /**/ static int -putpromptchar(int doprint, int endchar, unsigned int *txtchangep) +putpromptchar(int doprint, int endchar, zattr *txtchangep) { char *ss, *hostnam; int t0, arg, test, sep, j, numjobs, len; + zattr atr; struct tm *tm; struct timespec ts; time_t timet; @@ -538,13 +539,13 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY); break; case 'F': - arg = parsecolorchar(arg, 1); - if (arg >= 0 && !(arg & TXTNOFGCOLOUR)) { - txtchangeset(txtchangep, arg & TXT_ATTR_FG_ON_MASK, + atr = parsecolorchar(arg, 1); + if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) { + txtchangeset(txtchangep, atr & TXT_ATTR_FG_ON_MASK, TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK); txtunset(TXT_ATTR_FG_COL_MASK); - txtset(arg & TXT_ATTR_FG_ON_MASK); - set_colour_attribute(arg, COL_SEQ_FG, TSC_PROMPT); + txtset(atr & TXT_ATTR_FG_ON_MASK); + set_colour_attribute(atr, COL_SEQ_FG, TSC_PROMPT); break; } /* else FALLTHROUGH */ @@ -554,13 +555,13 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep) set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT); break; case 'K': - arg = parsecolorchar(arg, 0); - if (arg >= 0 && !(arg & TXTNOBGCOLOUR)) { - txtchangeset(txtchangep, arg & TXT_ATTR_BG_ON_MASK, + atr = parsecolorchar(arg, 0); + if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) { + txtchangeset(txtchangep, atr & TXT_ATTR_BG_ON_MASK, TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK); txtunset(TXT_ATTR_BG_COL_MASK); - txtset(arg & TXT_ATTR_BG_ON_MASK); - set_colour_attribute(arg, COL_SEQ_BG, TSC_PROMPT); + txtset(atr & TXT_ATTR_BG_ON_MASK); + set_colour_attribute(atr, COL_SEQ_BG, TSC_PROMPT); break; } /* else FALLTHROUGH */ @@ -1185,7 +1186,7 @@ countprompt(char *str, int *wp, int *hp, int overf) /**/ static int prompttrunc(int arg, int truncchar, int doprint, int endchar, - unsigned int *txtchangep) + zattr *txtchangep) { if (arg > 0) { char ch = *bv->fm, *ptr, *truncstr; @@ -1567,8 +1568,8 @@ static const char *ansi_colours[] = { /* Defines the available types of highlighting */ struct highlight { const char *name; - int mask_on; - int mask_off; + zattr mask_on; + zattr mask_off; }; static const struct highlight highlights[] = { @@ -1615,11 +1616,21 @@ match_named_colour(const char **teststrp) */ /**/ -mod_export int +mod_export zattr match_colour(const char **teststrp, int is_fg, int colour) { - int shft, on, named = 0, tc; + int shft, named = 0, tc; + zattr on; + if (is_fg) { + shft = TXT_ATTR_FG_COL_SHIFT; + on = TXTFGCOLOUR; + tc = TCFGCOLOUR; + } else { + shft = TXT_ATTR_BG_COL_SHIFT; + on = TXTBGCOLOUR; + tc = TCBGCOLOUR; + } if (teststrp) { if (**teststrp == '#' && isxdigit((*teststrp)[1])) { struct color_rgb color; @@ -1637,7 +1648,12 @@ match_colour(const char **teststrp, int is_fg, int colour) color.blue = col & 0xff; } *teststrp = end; - colour = runhookdef(GETCOLORATTR, &color); + colour = runhookdef(GETCOLORATTR, &color) - 1; + if (colour < 0) { /* no hook function added, try true color (24-bit) */ + colour = (((color.red << 8) + color.green) << 8) + color.blue; + return on | (is_fg ? TXT_ATTR_FG_24BIT : TXT_ATTR_BG_24BIT) | + (zattr)colour << shft; + } } else if ((named = ialpha(**teststrp))) { colour = match_named_colour(teststrp); if (colour == 8) { @@ -1645,22 +1661,14 @@ match_colour(const char **teststrp, int is_fg, int colour) return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR; } } - else + else { colour = (int)zstrtol(*teststrp, (char **)teststrp, 10); - } - if (colour < 0 || colour >= 256) - return -1; - if (is_fg) { - shft = TXT_ATTR_FG_COL_SHIFT; - on = TXTFGCOLOUR; - tc = TCFGCOLOUR; - } else { - shft = TXT_ATTR_BG_COL_SHIFT; - on = TXTBGCOLOUR; - tc = TCBGCOLOUR; + if (colour < 0 || colour >= 256) + return TXT_ERROR; + } } /* - * Try termcap for numbered characters if posible. + * Try termcap for numbered characters if possible. * Don't for named characters, since our best bet * of getting the names right is with ANSI sequences. */ @@ -1671,7 +1679,7 @@ match_colour(const char **teststrp, int is_fg, int colour) * Can we assume ANSI colours work? */ if (colour > 7) - return -1; /* No. */ + return TXT_ERROR; /* No. */ } else { /* * We can handle termcap colours and the number @@ -1681,7 +1689,7 @@ match_colour(const char **teststrp, int is_fg, int colour) TXT_ATTR_BG_TERMCAP; } } - return on | (colour << shft); + return on | (zattr)colour << shft; } /* @@ -1691,7 +1699,7 @@ match_colour(const char **teststrp, int is_fg, int colour) /**/ mod_export void -match_highlight(const char *teststr, int *on_var) +match_highlight(const char *teststr, zattr *on_var) { int found = 1; @@ -1701,7 +1709,8 @@ match_highlight(const char *teststr, int *on_var) found = 0; if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) { - int is_fg = (teststr[0] == 'f'), atr; + int is_fg = (teststr[0] == 'f'); + zattr atr; teststr += 3; atr = match_colour(&teststr, is_fg, 0); @@ -1711,7 +1720,7 @@ match_highlight(const char *teststr, int *on_var) break; found = 1; /* skip out of range colours but keep scanning attributes */ - if (atr >= 0) + if (atr != TXT_ERROR) *on_var |= atr; } else { for (hl = highlights; hl->name; hl++) { @@ -1776,7 +1785,7 @@ output_colour(int colour, int fg_bg, int use_tc, char *buf) /**/ mod_export int -output_highlight(int atr, char *buf) +output_highlight(zattr atr, char *buf) { const struct highlight *hp; int atrlen = 0, len; @@ -1939,7 +1948,8 @@ allocate_colour_buffer(void) strlen(fg_bg_sequences[COL_SEQ_BG].end); len = lenfg > lenbg ? lenfg : lenbg; - colseq_buf = (char *)zalloc(len+1); + /* add 1 for the null and 14 for truecolor */ + colseq_buf = (char *)zalloc(len+15); } /* Free the colour buffer previously allocated. */ @@ -1970,21 +1980,23 @@ free_colour_buffer(void) /**/ mod_export void -set_colour_attribute(int atr, int fg_bg, int flags) +set_colour_attribute(zattr atr, int fg_bg, int flags) { char *ptr; int do_free, is_prompt = (flags & TSC_PROMPT) ? 1 : 0; - int colour, tc, def, use_termcap; + int colour, tc, def, use_termcap, use_truecolor; if (fg_bg == COL_SEQ_FG) { colour = txtchangeget(atr, TXT_ATTR_FG_COL); tc = TCFGCOLOUR; def = txtchangeisset(atr, TXTNOFGCOLOUR); + use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT); use_termcap = txtchangeisset(atr, TXT_ATTR_FG_TERMCAP); } else { colour = txtchangeget(atr, TXT_ATTR_BG_COL); tc = TCBGCOLOUR; def = txtchangeisset(atr, TXTNOBGCOLOUR); + use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT); use_termcap = txtchangeisset(atr, TXT_ATTR_BG_TERMCAP); } @@ -1992,12 +2004,13 @@ set_colour_attribute(int atr, int fg_bg, int flags) * If we're not restoring the default, and either have a * colour value that is too large for ANSI, or have been told * to use the termcap sequence, try to use the termcap sequence. + * True color is not covered by termcap. * * We have already sanitised the values we allow from the * highlighting variables, so much of this shouldn't be * necessary at this point, but we might as well be safe. */ - if (!def && (colour > 7 || use_termcap)) { + if (!def && !use_truecolor && (colour > 7 || use_termcap)) { /* * We can if it's available, and either we couldn't get * the maximum number of colours, or the colour is in range. @@ -2041,6 +2054,9 @@ set_colour_attribute(int atr, int fg_bg, int flags) strcpy(ptr, fg_bg_sequences[fg_bg].def); while (*ptr) ptr++; + } else if (use_truecolor) { + ptr += sprintf(ptr, "8;2;%d;%d;%d", colour >> 16, + (colour >> 8) & 0xff, colour & 0xff); } else *ptr++ = colour + '0'; strcpy(ptr, fg_bg_sequences[fg_bg].end); diff --git a/Src/zsh.h b/Src/zsh.h index 68731e226..10897372b 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2633,6 +2633,12 @@ struct ttyinfo { * Text attributes for displaying in ZLE */ +#ifdef HAVE_STDINT_H + typedef uint64_t zattr; +#else + typedef zulong zattr; +#endif + #define TXTBOLDFACE 0x0001 #define TXTSTANDOUT 0x0002 #define TXTUNDERLINE 0x0004 @@ -2664,32 +2670,41 @@ struct ttyinfo { */ #define TXT_MULTIWORD_MASK 0x0400 +/* used when, e.g an invalid colour is specified */ +#define TXT_ERROR 0x0800 + /* Mask for colour to use in foreground */ -#define TXT_ATTR_FG_COL_MASK 0x000FF000 +#define TXT_ATTR_FG_COL_MASK 0x000000FFFFFF0000 /* Bits to shift the foreground colour */ -#define TXT_ATTR_FG_COL_SHIFT (12) +#define TXT_ATTR_FG_COL_SHIFT (16) /* Mask for colour to use in background */ -#define TXT_ATTR_BG_COL_MASK 0x0FF00000 +#define TXT_ATTR_BG_COL_MASK 0xFFFFFF0000000000 /* Bits to shift the background colour */ -#define TXT_ATTR_BG_COL_SHIFT (20) +#define TXT_ATTR_BG_COL_SHIFT (40) /* Flag to use termcap AF sequence to set colour, if available */ -#define TXT_ATTR_FG_TERMCAP 0x10000000 +#define TXT_ATTR_FG_TERMCAP 0x1000 /* Flag to use termcap AB sequence to set colour, if available */ -#define TXT_ATTR_BG_TERMCAP 0x20000000 +#define TXT_ATTR_BG_TERMCAP 0x2000 + +/* Flag to indicate that foreground is a 24-bit colour */ +#define TXT_ATTR_FG_24BIT 0x4000 +/* Flag to indicate that background is a 24-bit colour */ +#define TXT_ATTR_BG_24BIT 0x8000 /* Things to turn on, including values for the colour elements */ #define TXT_ATTR_ON_VALUES_MASK \ (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\ - TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP) + TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP|\ + TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT) /* Mask out everything to do with setting a foreground colour */ #define TXT_ATTR_FG_ON_MASK \ - (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_TERMCAP) + (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_TERMCAP|TXT_ATTR_FG_24BIT) /* Mask out everything to do with setting a background colour */ #define TXT_ATTR_BG_ON_MASK \ - (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_TERMCAP) + (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_TERMCAP|TXT_ATTR_BG_24BIT) /* Mask out everything to do with activating colours */ #define TXT_ATTR_COLOUR_ON_MASK \ diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 8289ee97c..e7d529b6e 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -137,6 +137,10 @@ char *alloca _((size_t)); #include #endif +#ifdef HAVE_STDINT_H +# include +#endif + #include #include #include -- cgit 1.4.1