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:
129
terminal.c
129
terminal.c
@ -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);
|
||||
|
Reference in New Issue
Block a user