1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-06-30 19:12:48 -05:00

Support ESC[38;2;R;G;Bm for 24-bit true colour.

This is a heavily rewritten version of a patch originally by Lorenz
Diener; it was tidied up somewhat by Christian Brabandt, and then
tidied up more by me. The basic idea is to add to the termchar
structure a pair of small structs encoding 24-bit RGB values, each
with a flag indicating whether it's turned on; if it is, it overrides
any other specification of fg or bg colour for that character cell.

I've added a test line to colours.txt containing a few example colours
from /usr/share/X11/rgb.txt. In fact it makes quite a good demo to run
the whole of rgb.txt through this treatment, with a command such as

  perl -pe 's!^\s*(\d+)\s+(\d+)\s+(\d+).*$!\e[38;2;$1;$2;$3m$&\e[m!' rgb.txt
This commit is contained in:
Simon Tatham
2017-09-30 17:32:32 +01:00
parent 581dd7071e
commit a4cbd3dfdb
8 changed files with 204 additions and 34 deletions

View File

@ -108,6 +108,7 @@ static void update_sbar(Terminal *);
static void deselect(Terminal *);
static void term_print_finish(Terminal *);
static void scroll(Terminal *, int, int, int, int);
static void parse_optionalrgb(optionalrgb *out, unsigned *values);
#ifdef OPTIMISE_SCROLL
static void scroll_display(Terminal *, int, int, int);
#endif /* OPTIMISE_SCROLL */
@ -283,6 +284,8 @@ static int termchars_equal_override(termchar *a, termchar *b,
unsigned long bchr, unsigned long battr)
{
/* FULL-TERMCHAR */
if (!truecolour_equal(a->truecolour, b->truecolour))
return FALSE;
if (a->chr != bchr)
return FALSE;
if ((a->attr &~ DATTR_MASK) != (battr &~ DATTR_MASK))
@ -607,6 +610,24 @@ static void makeliteral_attr(struct buf *b, termchar *c, unsigned long *state)
add(b, (unsigned char)(attr & 0xFF));
}
}
static void makeliteral_truecolour(struct buf *b, termchar *c, unsigned long *state)
{
/*
* Put the used parts of the colour info into the buffer.
*/
add(b, ((c->truecolour.fg.enabled ? 1 : 0) |
(c->truecolour.bg.enabled ? 2 : 0)));
if (c->truecolour.fg.enabled) {
add(b, c->truecolour.fg.r);
add(b, c->truecolour.fg.g);
add(b, c->truecolour.fg.b);
}
if (c->truecolour.bg.enabled) {
add(b, c->truecolour.bg.r);
add(b, c->truecolour.bg.g);
add(b, c->truecolour.bg.b);
}
}
static void makeliteral_cc(struct buf *b, termchar *c, unsigned long *state)
{
/*
@ -681,6 +702,7 @@ static unsigned char *compressline(termline *ldata)
*/
makerle(b, ldata, makeliteral_chr);
makerle(b, ldata, makeliteral_attr);
makerle(b, ldata, makeliteral_truecolour);
makerle(b, ldata, makeliteral_cc);
/*
@ -826,6 +848,29 @@ static void readliteral_attr(struct buf *b, termchar *c, termline *ldata,
c->attr = attr;
}
static void readliteral_truecolour(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
{
int flags = get(b);
if (flags & 1) {
c->truecolour.fg.enabled = TRUE;
c->truecolour.fg.r = get(b);
c->truecolour.fg.g = get(b);
c->truecolour.fg.b = get(b);
} else {
c->truecolour.fg = optionalrgb_none;
}
if (flags & 2) {
c->truecolour.bg.enabled = TRUE;
c->truecolour.bg.r = get(b);
c->truecolour.bg.g = get(b);
c->truecolour.bg.b = get(b);
} else {
c->truecolour.bg = optionalrgb_none;
}
}
static void readliteral_cc(struct buf *b, termchar *c, termline *ldata,
unsigned long *state)
{
@ -899,6 +944,7 @@ static termline *decompressline(unsigned char *data, int *bytes_used)
*/
readrle(b, ldata, readliteral_chr);
readrle(b, ldata, readliteral_attr);
readrle(b, ldata, readliteral_truecolour);
readrle(b, ldata, readliteral_cc);
/* Return the number of bytes read, for diagnostic purposes. */
@ -1570,6 +1616,8 @@ void term_clrsb(Terminal *term)
update_sbar(term);
}
const optionalrgb optionalrgb_none = {0, 0, 0, 0};
/*
* Initialise the terminal.
*/
@ -1646,6 +1694,8 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata,
term->basic_erase_char.chr = CSET_ASCII | ' ';
term->basic_erase_char.attr = ATTR_DEFAULT;
term->basic_erase_char.cc_next = 0;
term->basic_erase_char.truecolour.fg = optionalrgb_none;
term->basic_erase_char.truecolour.bg = optionalrgb_none;
term->erase_char = term->basic_erase_char;
return term;
@ -3212,6 +3262,8 @@ static void term_out(Terminal *term)
clear_cc(cline, term->curs.x);
cline->chars[term->curs.x].chr = c;
cline->chars[term->curs.x].attr = term->curr_attr;
cline->chars[term->curs.x].truecolour =
term->curr_truecolour;
term->curs.x++;
@ -3219,6 +3271,8 @@ static void term_out(Terminal *term)
clear_cc(cline, term->curs.x);
cline->chars[term->curs.x].chr = UCSWIDE;
cline->chars[term->curs.x].attr = term->curr_attr;
cline->chars[term->curs.x].truecolour =
term->curr_truecolour;
break;
case 1:
@ -3229,6 +3283,8 @@ static void term_out(Terminal *term)
clear_cc(cline, term->curs.x);
cline->chars[term->curs.x].chr = c;
cline->chars[term->curs.x].attr = term->curr_attr;
cline->chars[term->curs.x].truecolour =
term->curr_truecolour;
break;
case 0:
@ -3799,6 +3855,8 @@ static void term_out(Terminal *term)
switch (def(term->esc_args[i], 0)) {
case 0: /* restore defaults */
term->curr_attr = term->default_attr;
term->curr_truecolour =
term->basic_erase_char.truecolour;
break;
case 1: /* enable bold */
compatibility(VT100AVO);
@ -3860,6 +3918,7 @@ static void term_out(Terminal *term)
case 36:
case 37:
/* foreground */
term->curr_truecolour.fg.enabled = FALSE;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |=
(term->esc_args[i] - 30)<<ATTR_FGSHIFT;
@ -3873,12 +3932,14 @@ static void term_out(Terminal *term)
case 96:
case 97:
/* aixterm-style bright foreground */
term->curr_truecolour.fg.enabled = FALSE;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |=
((term->esc_args[i] - 90 + 8)
<< ATTR_FGSHIFT);
break;
case 39: /* default-foreground */
term->curr_truecolour.fg.enabled = FALSE;
term->curr_attr &= ~ATTR_FGMASK;
term->curr_attr |= ATTR_DEFFG;
break;
@ -3891,6 +3952,7 @@ static void term_out(Terminal *term)
case 46:
case 47:
/* background */
term->curr_truecolour.bg.enabled = FALSE;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |=
(term->esc_args[i] - 40)<<ATTR_BGSHIFT;
@ -3904,16 +3966,32 @@ static void term_out(Terminal *term)
case 106:
case 107:
/* aixterm-style bright background */
term->curr_truecolour.bg.enabled = FALSE;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |=
((term->esc_args[i] - 100 + 8)
<< ATTR_BGSHIFT);
break;
case 49: /* default-background */
term->curr_truecolour.bg.enabled = FALSE;
term->curr_attr &= ~ATTR_BGMASK;
term->curr_attr |= ATTR_DEFBG;
break;
case 38: /* xterm 256-colour mode */
/*
* 256-colour and true-colour
* sequences. A 256-colour
* foreground is selected by a
* sequence of 3 arguments in the
* form 38;5;n, where n is in the
* range 0-255. A true-colour RGB
* triple is selected by 5 args of
* the form 38;2;r;g;b. Replacing
* the initial 38 with 48 in both
* cases selects the same colour
* as the background.
*/
case 38:
if (i+2 < term->esc_nargs &&
term->esc_args[i+1] == 5) {
term->curr_attr &= ~ATTR_FGMASK;
@ -3921,9 +3999,16 @@ static void term_out(Terminal *term)
((term->esc_args[i+2] & 0xFF)
<< ATTR_FGSHIFT);
i += 2;
}
if (i + 4 < term->esc_nargs &&
term->esc_args[i + 1] == 2) {
parse_optionalrgb(
&term->curr_truecolour.fg,
term->esc_args + (i+2));
i += 4;
}
break;
case 48: /* xterm 256-colour mode */
case 48:
if (i+2 < term->esc_nargs &&
term->esc_args[i+1] == 5) {
term->curr_attr &= ~ATTR_BGMASK;
@ -3932,6 +4017,13 @@ static void term_out(Terminal *term)
<< ATTR_BGSHIFT);
i += 2;
}
if (i + 4 < term->esc_nargs &&
term->esc_args[i+1] == 2) {
parse_optionalrgb(
&term->curr_truecolour.bg,
term->esc_args + (i+2));
i += 4;
}
break;
}
}
@ -4733,6 +4825,19 @@ static void term_out(Terminal *term)
logflush(term->logctx);
}
/*
* Small subroutine to parse three consecutive escape-sequence
* arguments representing a true-colour RGB triple into an
* optionalrgb.
*/
static void parse_optionalrgb(optionalrgb *out, unsigned *values)
{
out->enabled = TRUE;
out->r = values[0] < 256 ? values[0] : 0;
out->g = values[1] < 256 ? values[1] : 0;
out->b = values[2] < 256 ? values[2] : 0;
}
/*
* To prevent having to run the reasonably tricky bidi algorithm
* too many times, we maintain a cache of the last lineful of data
@ -5035,6 +5140,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
int last_run_dirty = 0;
int laststart, dirtyrect;
int *backward;
truecolour tc;
scrpos.y = i + term->disptop;
ldata = lineptr(scrpos.y);
@ -5064,6 +5170,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) |
ATTR_DEFFG | ATTR_DEFBG;
tc = d->truecolour;
if (!term->xterm_256_colour) {
int colour;
colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;
@ -5131,6 +5238,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
/* FULL-TERMCHAR */
newline[j].attr = tattr;
newline[j].chr = tchar;
newline[j].truecolour = tc;
/* Combining characters are still read from lchars */
newline[j].cc_next = 0;
}
@ -5181,6 +5289,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
term->disptext[i]->lattr);
term->disptext[i]->lattr = ldata->lattr;
tc = term->erase_char.truecolour;
for (j = 0; j < term->cols; j++) {
unsigned long tattr, tchar;
int break_run, do_copy;
@ -5194,6 +5303,9 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
break_run = ((tattr ^ attr) & term->attr_mask) != 0;
if (!truecolour_equal(newline[j].truecolour, tc))
break_run = TRUE;
#ifdef USES_VTLINE_HACK
/* Special hack for VT100 Linedraw glyphs */
if ((tchar >= 0x23BA && tchar <= 0x23BD) ||
@ -5226,15 +5338,15 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
if (break_run) {
if ((dirty_run || last_run_dirty) && ccount > 0) {
do_text(ctx, start, i, ch, ccount, attr,
ldata->lattr);
do_text(ctx, start, i, ch, ccount, attr, ldata->lattr, tc);
if (attr & (TATTR_ACTCURS | TATTR_PASCURS))
do_cursor(ctx, start, i, ch, ccount, attr,
ldata->lattr);
ldata->lattr, tc);
}
start = j;
ccount = 0;
attr = tattr;
tc = newline[j].truecolour;
cset = CSET_OF(tchar);
if (term->ucsdata->dbcs_screenfont)
last_run_dirty = dirty_run;
@ -5303,6 +5415,7 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
copy_termchar(term->disptext[i], j, d);
term->disptext[i]->chars[j].chr = tchar;
term->disptext[i]->chars[j].attr = tattr;
term->disptext[i]->chars[j].truecolour = tc;
if (start == j)
term->disptext[i]->chars[j].attr |= DATTR_STARTRUN;
}
@ -5324,11 +5437,9 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
}
}
if (dirty_run && ccount > 0) {
do_text(ctx, start, i, ch, ccount, attr,
ldata->lattr);
do_text(ctx, start, i, ch, ccount, attr, ldata->lattr, tc);
if (attr & (TATTR_ACTCURS | TATTR_PASCURS))
do_cursor(ctx, start, i, ch, ccount, attr,
ldata->lattr);
do_cursor(ctx, start, i, ch, ccount, attr, ldata->lattr, tc);
}
unlineptr(ldata);