ATTR_NARROW)) {
(ATTR_NARROW | DATTR_MASK))) {
static void do_paint(Terminal *term, Context ctx, int may_optimise){    int i, j, our_curs_y, our_curs_x;    int rv, cursor;    pos scrpos;    wchar_t *ch;    int chlen;    termchar cursor_background;#ifdef OPTIMISE_SCROLL    struct scrollregion *sr;#endif /* OPTIMISE_SCROLL */    termchar *newline;    cursor_background = term->basic_erase_char;    chlen = 1024;    ch = snewn(chlen, wchar_t);    newline = snewn(term->cols, termchar);    rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);    /* Depends on:     * screen array, disptop, scrtop,     * selection, rv,      * cfg.blinkpc, blink_is_real, tblinker,      * curs.y, curs.x, cblinker, cfg.blink_cur, cursor_on, has_focus, wrapnext     */    /* Has the cursor position or type changed ? */    if (term->cursor_on) {	if (term->has_focus) {	    if (term->cblinker || !term->cfg.blink_cur)		cursor = TATTR_ACTCURS;	    else		cursor = 0;	} else	    cursor = TATTR_PASCURS;	if (term->wrapnext)	    cursor |= TATTR_RIGHTCURS;    } else	cursor = 0;    our_curs_y = term->curs.y - term->disptop;    {	/*	 * Adjust the cursor position:	 *  - for bidi	 *  - in the case where it's resting on the right-hand half	 *    of a CJK wide character. xterm's behaviour here,	 *    which seems adequate to me, is to display the cursor	 *    covering the _whole_ character, exactly as if it were	 *    one space to the left.	 */	termline *ldata = lineptr(term->curs.y);	termchar *lchars;	our_curs_x = term->curs.x;	if ( (lchars = term_bidi_line(term, ldata, our_curs_y)) != NULL) {	    our_curs_x = term->post_bidi_cache[our_curs_y].forward[our_curs_x];	} else	    lchars = ldata->chars;	if (our_curs_x > 0 &&	    lchars[our_curs_x].chr == UCSWIDE)	    our_curs_x--;	unlineptr(ldata);    }    /*     * If the cursor is not where it was last time we painted, and     * its previous position is visible on screen, invalidate its     * previous position.     */    if (term->dispcursy >= 0 &&	(term->curstype != cursor ||	 term->dispcursy != our_curs_y ||	 term->dispcursx != our_curs_x)) {	termchar *dispcurs = term->disptext[term->dispcursy]->chars +	    term->dispcursx;	if (term->dispcursx > 0 && dispcurs->chr == UCSWIDE)	    dispcurs[-1].attr |= ATTR_INVALID;	if (term->dispcursx < term->cols-1 && dispcurs[1].chr == UCSWIDE)	    dispcurs[1].attr |= ATTR_INVALID;	dispcurs->attr |= ATTR_INVALID;	term->curstype = 0;    }    term->dispcursx = term->dispcursy = -1;#ifdef OPTIMISE_SCROLL    /* Do scrolls */    sr = term->scrollhead;    while (sr) {	struct scrollregion *next = sr->next;	do_scroll(ctx, sr->topline, sr->botline, sr->lines);	sfree(sr);	sr = next;    }    term->scrollhead = term->scrolltail = NULL;#endif /* OPTIMISE_SCROLL */    /* The normal screen data */    for (i = 0; i < term->rows; i++) {	termline *ldata;	termchar *lchars;	int dirty_line, dirty_run, selected;	unsigned long attr = 0, cset = 0;	int updated_line = 0;	int start = 0;	int ccount = 0;	int last_run_dirty = 0;	int *backward;	scrpos.y = i + term->disptop;	ldata = lineptr(scrpos.y);	/* Do Arabic shaping and bidi. */	lchars = term_bidi_line(term, ldata, i);	if (lchars) {	    backward = term->post_bidi_cache[i].backward;	} else {	    lchars = ldata->chars;	    backward = NULL;	}	/*	 * First loop: work along the line deciding what we want	 * each character cell to look like.	 */	for (j = 0; j < term->cols; j++) {	    unsigned long tattr, tchar;	    termchar *d = lchars + j;	    scrpos.x = backward ? backward[j] : j;	    tchar = d->chr;	    tattr = d->attr;            if (!term->cfg.ansi_colour)                tattr = (tattr & ~(ATTR_FGMASK | ATTR_BGMASK)) |                 ATTR_DEFFG | ATTR_DEFBG;	    if (!term->cfg.xterm_256_colour) {		int colour;		colour = (tattr & ATTR_FGMASK) >> ATTR_FGSHIFT;		if (colour >= 16 && colour < 256)		    tattr = (tattr &~ ATTR_FGMASK) | ATTR_DEFFG;		colour = (tattr & ATTR_BGMASK) >> ATTR_BGSHIFT;		if (colour >= 16 && colour < 256)		    tattr = (tattr &~ ATTR_BGMASK) | ATTR_DEFBG;	    }	    switch (tchar & CSET_MASK) {	      case CSET_ASCII:		tchar = term->ucsdata->unitab_line[tchar & 0xFF];		break;	      case CSET_LINEDRW:		tchar = term->ucsdata->unitab_xterm[tchar & 0xFF];		break;	      case CSET_SCOACS:  		tchar = term->ucsdata->unitab_scoacs[tchar&0xFF]; 		break;	    }	    if (j < term->cols-1 && d[1].chr == UCSWIDE)		tattr |= ATTR_WIDE;	    /* Video reversing things */	    if (term->selstate == DRAGGING || term->selstate == SELECTED) {		if (term->seltype == LEXICOGRAPHIC)		    selected = (posle(term->selstart, scrpos) &&				poslt(scrpos, term->selend));		else		    selected = (posPle(term->selstart, scrpos) &&				posPlt(scrpos, term->selend));	    } else		selected = FALSE;	    tattr = (tattr ^ rv		     ^ (selected ? ATTR_REVERSE : 0));	    /* 'Real' blinking ? */	    if (term->blink_is_real && (tattr & ATTR_BLINK)) {		if (term->has_focus && term->tblinker) {		    tchar = term->ucsdata->unitab_line[(unsigned char)' '];		}		tattr &= ~ATTR_BLINK;	    }	    /*	     * Check the font we'll _probably_ be using to see if 	     * the character is wide when we don't want it to be.	     */	    if (tchar != term->disptext[i]->chars[j].chr ||		tattr != (term->disptext[i]->chars[j].attr &~			  ATTR_NARROW)) {		if ((tattr & ATTR_WIDE) == 0 && char_width(ctx, tchar) == 2)		    tattr |= ATTR_NARROW;	    } else if (term->disptext[i]->chars[j].attr & ATTR_NARROW)		tattr |= ATTR_NARROW;	    /* Cursor here ? Save the 'background' */	    if (i == our_curs_y && j == our_curs_x) {		/* FULL-TERMCHAR */		cursor_background.chr = tchar;		cursor_background.attr = tattr;		/* For once, this cc_next field is an absolute index in lchars */		if (d->cc_next)		    cursor_background.cc_next = d->cc_next + j;		else		    cursor_background.cc_next = 0;		term->dispcursx = j;		term->dispcursy = i;	    }	    /* FULL-TERMCHAR */	    newline[j].attr = tattr;	    newline[j].chr = tchar;	    /* Combining characters are still read from lchars */	    newline[j].cc_next = 0;	}	/*	 * Now loop over the line again, noting where things have	 * changed.	 */	for (j = 0; j < term->cols; j++) {	    if (term->disptext[i]->chars[j].chr != newline[j].chr ||		term->disptext[i]->chars[j].attr != newline[j].attr) {		term->disptext[i]->chars[j].attr |= ATTR_INVALID;	    }	}	/*	 * Finally, loop once more and actually do the drawing.	 */	dirty_run = dirty_line = (ldata->lattr !=				  term->disptext[i]->lattr);	term->disptext[i]->lattr = ldata->lattr;	for (j = 0; j < term->cols; j++) {	    unsigned long tattr, tchar;	    int break_run, do_copy;	    termchar *d = lchars + j;	    tattr = newline[j].attr;	    tchar = newline[j].chr;	    if ((term->disptext[i]->chars[j].attr ^ tattr) & ATTR_WIDE)		dirty_line = TRUE;	    break_run = ((tattr ^ attr) & term->attr_mask) != 0;	    /* Special hack for VT100 Linedraw glyphs */	    if (tchar >= 0x23BA && tchar <= 0x23BD)		break_run = TRUE;	    /*	     * Separate out sequences of characters that have the	     * same CSET, if that CSET is a magic one.	     */	    if (CSET_OF(tchar) != cset)		break_run = TRUE;	    /*	     * Break on both sides of any combined-character cell.	     */	    if (d->cc_next != 0 ||		(j > 0 && d[-1].cc_next != 0))		break_run = TRUE;	    if (!term->ucsdata->dbcs_screenfont && !dirty_line) {		if (term->disptext[i]->chars[j].chr == tchar &&		    term->disptext[i]->chars[j].attr == tattr)		    break_run = TRUE;		else if (!dirty_run && ccount == 1)		    break_run = TRUE;	    }	    if (break_run) {		if ((dirty_run || last_run_dirty) && ccount > 0) {		    do_text(ctx, start, i, ch, ccount, attr, ldata->lattr);		    updated_line = 1;		}		start = j;		ccount = 0;		attr = tattr;		cset = CSET_OF(tchar);		if (term->ucsdata->dbcs_screenfont)		    last_run_dirty = dirty_run;		dirty_run = dirty_line;	    }	    do_copy = FALSE;	    if (!termchars_equal_override(&term->disptext[i]->chars[j],					  d, tchar, tattr)) {		do_copy = TRUE;		dirty_run = TRUE;	    }	    if (ccount >= chlen) {		chlen = ccount + 256;		ch = sresize(ch, chlen, wchar_t);	    }	    ch[ccount++] = (wchar_t) tchar;	    if (d->cc_next) {		termchar *dd = d;		while (dd->cc_next) {		    unsigned long schar;		    dd += dd->cc_next;		    schar = dd->chr;		    switch (schar & CSET_MASK) {		      case CSET_ASCII:			schar = term->ucsdata->unitab_line[schar & 0xFF];			break;		      case CSET_LINEDRW:			schar = term->ucsdata->unitab_xterm[schar & 0xFF];			break;		      case CSET_SCOACS:			schar = term->ucsdata->unitab_scoacs[schar&0xFF];			break;		    }		    if (ccount >= chlen) {			chlen = ccount + 256;			ch = sresize(ch, chlen, wchar_t);		    }		    ch[ccount++] = (wchar_t) schar;		}		attr |= TATTR_COMBINING;	    }	    if (do_copy) {		copy_termchar(term->disptext[i], j, d);		term->disptext[i]->chars[j].chr = tchar;		term->disptext[i]->chars[j].attr = tattr;	    }	    /* If it's a wide char step along to the next one. */	    if (tattr & ATTR_WIDE) {		if (++j < term->cols) {		    d++;		    /*		     * By construction above, the cursor should not		     * be on the right-hand half of this character.		     * Ever.		     */		    assert(!(i == our_curs_y && j == our_curs_x));		    if (!termchars_equal(&term->disptext[i]->chars[j], d))			dirty_run = TRUE;		    copy_termchar(term->disptext[i], j, d);		}	    }	}	if (dirty_run && ccount > 0) {	    do_text(ctx, start, i, ch, ccount, attr, ldata->lattr);	    updated_line = 1;	}	/* Cursor on this line ? (and changed) */	if (i == our_curs_y && (term->curstype != cursor || updated_line)) {	    ch[0] = (wchar_t) cursor_background.chr;	    attr = cursor_background.attr | cursor;	    ccount = 1;	    if (cursor_background.cc_next) {		termchar *dd = ldata->chars + cursor_background.cc_next;		while (1) {		    unsigned long schar;		    schar = dd->chr;		    switch (schar & CSET_MASK) {		      case CSET_ASCII:			schar = term->ucsdata->unitab_line[schar & 0xFF];			break;		      case CSET_LINEDRW:			schar = term->ucsdata->unitab_xterm[schar & 0xFF];			break;		      case CSET_SCOACS:			schar = term->ucsdata->unitab_scoacs[schar&0xFF];			break;		    }		    if (ccount >= chlen) {			chlen = ccount + 256;			ch = sresize(ch, chlen, wchar_t);		    }		    ch[ccount++] = (wchar_t) schar;		    if (dd->cc_next)			dd += dd->cc_next;		    else			break;		}		attr |= TATTR_COMBINING;	    }	    do_cursor(ctx, our_curs_x, i, ch, ccount, attr, ldata->lattr);	    term->curstype = cursor;	}	unlineptr(ldata);    }    sfree(newline);    sfree(ch);}