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
Родитель 581dd7071e
Коммит a4cbd3dfdb
8 изменённых файлов: 204 добавлений и 34 удалений

Просмотреть файл

@ -3,7 +3,8 @@ PuTTY is copyright 1997-2017 Simon Tatham.
Portions copyright Robert de Bath, Joris van Rantwijk, Delian
Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus
Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.
Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian
Brabandt, and CORE SDI S.A.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files

Просмотреть файл

@ -45,7 +45,7 @@ int from_backend(void *frontend, int is_stderr, const char *data, int len)
void request_resize(void *frontend, int x, int y) { }
void do_text(Context ctx, int x, int y, wchar_t * text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour tc)
{
int i;
@ -56,7 +56,7 @@ void do_text(Context ctx, int x, int y, wchar_t * text, int len,
printf("\n");
}
void do_cursor(Context ctx, int x, int y, wchar_t * text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour tc)
{
int i;

28
putty.h
Просмотреть файл

@ -592,12 +592,36 @@ void prompt_ensure_result_size(prompt_t *pr, int len);
/* Burn the evidence. (Assumes _all_ strings want free()ing.) */
void free_prompts(prompts_t *p);
/*
* Data type definitions for true-colour terminal display.
* 'optionalrgb' describes a single RGB colour, which overrides the
* other colour settings if 'enabled' is nonzero, and is ignored
* otherwise. 'truecolour' contains a pair of those for foreground and
* background.
*/
typedef struct optionalrgb {
unsigned char enabled;
unsigned char r, g, b;
} optionalrgb;
extern const optionalrgb optionalrgb_none;
typedef struct truecolour {
optionalrgb fg, bg;
} truecolour;
#define optionalrgb_equal(r1,r2) ( \
(r1).enabled==(r2).enabled && \
(r1).r==(r2).r && (r1).g==(r2).g && (r1).b==(r2).b)
#define truecolour_equal(c1,c2) ( \
optionalrgb_equal((c1).fg, (c2).fg) && \
optionalrgb_equal((c1).bg, (c2).bg))
/*
* Exports from the front end.
*/
void request_resize(void *frontend, int, int);
void do_text(Context, int, int, wchar_t *, int, unsigned long, int);
void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int);
void do_text(Context, int, int, wchar_t *, int, unsigned long, int,
truecolour);
void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int,
truecolour);
int char_width(Context ctx, int uc);
#ifdef OPTIMISE_SCROLL
void do_scroll(Context, int, int, int);

Просмотреть файл

@ -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);

Просмотреть файл

@ -40,6 +40,7 @@ struct termchar {
*/
unsigned long chr;
unsigned long attr;
truecolour truecolour;
/*
* The cc_next field is used to link multiple termchars
@ -102,6 +103,7 @@ struct terminal_tag {
#endif /* OPTIMISE_SCROLL */
int default_attr, curr_attr, save_attr;
truecolour curr_truecolour;
termchar basic_erase_char, erase_char;
bufchain inbuf; /* terminal input buffer */

1
testdata/colours.txt поставляемый
Просмотреть файл

@ -12,3 +12,4 @@ xterm 256: greys[48;5;236
a0a1a2a3a4a5a6a7 a8a9aaabacadaeaf b0b1b2b3b4b5b6b7 b8b9babbbcbdbebf
c0c1c2c3c4c5c6c7 c8c9cacbcccdcecf d0d1d2d3d4d5d6d7 d8d9dadbdcdddedf
e0e1e2e3e4e5e6e7 e8e9eaebecedeeef f0f1f2f3f4f5f6f7 f8f9fafbfcfdfeff
24-bit colour: SlateGrey OliveDrab goldenrod SaddleBrown DarkViolet (bg)

Просмотреть файл

@ -3029,6 +3029,24 @@ static void draw_set_colour(struct draw_ctx *dctx, int col)
#endif
}
static void draw_set_colour_rgb(struct draw_ctx *dctx, optionalrgb orgb)
{
#ifdef DRAW_TEXT_GDK
if (dctx->uctx.type == DRAWTYPE_GDK) {
GdkColor color;
color.red = orgb.r * 256;
color.green = orgb.g * 256;
color.blue = orgb.b * 256;
gdk_gc_set_rgb_fg_color(dctx->uctx.u.gdk.gc, &color);
}
#endif
#ifdef DRAW_TEXT_CAIRO
if (dctx->uctx.type == DRAWTYPE_CAIRO)
cairo_set_source_rgb(dctx->uctx.u.cairo.cr,
orgb.r / 255.0, orgb.g / 255.0, orgb.b / 255.0);
#endif
}
static void draw_rectangle(struct draw_ctx *dctx, int filled,
int x, int y, int w, int h)
{
@ -3222,7 +3240,7 @@ static void draw_backing_rect(struct gui_data *inst)
* We are allowed to fiddle with the contents of `text'.
*/
void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
struct gui_data *inst = dctx->inst;
@ -3316,13 +3334,19 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
((lattr & LATTR_MODE) == LATTR_BOT));
}
draw_set_colour(dctx, nbg);
if (truecolour.bg.enabled)
draw_set_colour_rgb(dctx, truecolour.bg);
else
draw_set_colour(dctx, nbg);
draw_rectangle(dctx, TRUE,
x*inst->font_width+inst->window_border,
y*inst->font_height+inst->window_border,
rlen*widefactor*inst->font_width, inst->font_height);
draw_set_colour(dctx, nfg);
if (truecolour.fg.enabled)
draw_set_colour_rgb(dctx, truecolour.fg);
else
draw_set_colour(dctx, nfg);
if (ncombining > 1) {
assert(len == 1);
unifont_draw_combining(&dctx->uctx, inst->fonts[fontid],
@ -3362,13 +3386,13 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
}
void do_text(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
struct gui_data *inst = dctx->inst;
int widefactor;
do_text_internal(ctx, x, y, text, len, attr, lattr);
do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
if (attr & ATTR_WIDE) {
widefactor = 2;
@ -3392,7 +3416,7 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
}
void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour truecolour)
{
struct draw_ctx *dctx = (struct draw_ctx *)ctx;
struct gui_data *inst = dctx->inst;
@ -3409,7 +3433,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
active = 1;
} else
active = 0;
do_text_internal(ctx, x, y, text, len, attr, lattr);
do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
if (attr & TATTR_COMBINING)
len = 1;

Просмотреть файл

@ -3395,7 +3395,7 @@ static void sys_cursor_update(void)
* We are allowed to fiddle with the contents of `text'.
*/
void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour truecolour)
{
COLORREF fg, bg, t;
int nfg, nbg, nfont;
@ -3522,8 +3522,16 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
if (nbg < 16) nbg |= 8;
else if (nbg >= 256) nbg |= 1;
}
fg = colours[nfg];
bg = colours[nbg];
if (truecolour.fg.enabled)
fg = RGB(truecolour.fg.r, truecolour.fg.g, truecolour.fg.b);
else
fg = colours[nfg];
if (truecolour.bg.enabled)
bg = RGB(truecolour.bg.r, truecolour.bg.g, truecolour.bg.b);
else
bg = colours[nbg];
SelectObject(hdc, fonts[nfont]);
SetTextColor(hdc, fg);
SetBkColor(hdc, bg);
@ -3768,7 +3776,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
* Wrapper that handles combining characters.
*/
void do_text(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour truecolour)
{
if (attr & TATTR_COMBINING) {
unsigned long a = 0;
@ -3778,13 +3786,13 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
len0 = 2;
if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) {
attr &= ~TATTR_COMBINING;
do_text_internal(ctx, x, y, text, len0+1, attr, lattr);
do_text_internal(ctx, x, y, text, len0+1, attr, lattr, truecolour);
text += len0+1;
len -= len0+1;
a = TATTR_COMBINING;
} else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) {
attr &= ~TATTR_COMBINING;
do_text_internal(ctx, x, y, text, len0+2, attr, lattr);
do_text_internal(ctx, x, y, text, len0+2, attr, lattr, truecolour);
text += len0+2;
len -= len0+2;
a = TATTR_COMBINING;
@ -3794,22 +3802,21 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
while (len--) {
if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) {
do_text_internal(ctx, x, y, text, 2, attr | a, lattr);
do_text_internal(ctx, x, y, text, 2, attr | a, lattr, truecolour);
len--;
text++;
} else {
do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
}
} else
do_text_internal(ctx, x, y, text, 1, attr | a, lattr, truecolour);
text++;
a = TATTR_COMBINING;
}
} else
do_text_internal(ctx, x, y, text, len, attr, lattr);
do_text_internal(ctx, x, y, text, len, attr, lattr, truecolour);
}
void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
unsigned long attr, int lattr)
unsigned long attr, int lattr, truecolour truecolour)
{
int fnt_width;
@ -3821,7 +3828,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
if (*text != UCSWIDE) {
do_text(ctx, x, y, text, len, attr, lattr);
do_text(ctx, x, y, text, len, attr, lattr, truecolour);
return;
}
ctype = 2;