+ long cursor_background = ERASE_CHAR;
+ unsigned long ticks;
+#ifdef OPTIMISE_SCROLL
+ struct scrollregion *sr;
+#endif /* OPTIMISE_SCROLL */
+
+ /*
+ * Check the visual bell state.
+ */
+ if (term->in_vbell) {
+ ticks = GETTICKCOUNT();
+ if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
+ term->in_vbell = FALSE;
+ }
+
+ 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, blinker, 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->blinker || !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 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.
+ */
+ unsigned long *ldata = lineptr(term->curs.y);
+ our_curs_x = term->curs.x;
+ if (our_curs_x > 0 &&
+ (ldata[our_curs_x] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+ our_curs_x--;
+ }
+
+ if (term->dispcurs && (term->curstype != cursor ||
+ term->dispcurs !=
+ term->disptext + our_curs_y * (term->cols + 1) +
+ our_curs_x)) {
+ if (term->dispcurs > term->disptext &&
+ (*term->dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+ term->dispcurs[-1] |= ATTR_INVALID;
+ if ( (term->dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+ term->dispcurs[1] |= ATTR_INVALID;
+ *term->dispcurs |= ATTR_INVALID;
+ term->curstype = 0;
+ }
+ term->dispcurs = NULL;
+
+#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++) {
+ unsigned long *ldata;
+ int lattr;
+ int idx, dirty_line, dirty_run, selected;
+ unsigned long attr = 0;
+ int updated_line = 0;
+ int start = 0;
+ int ccount = 0;
+ int last_run_dirty = 0;
+
+ scrpos.y = i + term->disptop;
+ ldata = lineptr(scrpos.y);
+ lattr = (ldata[term->cols] & LATTR_MODE);
+
+ idx = i * (term->cols + 1);
+ dirty_run = dirty_line = (ldata[term->cols] !=
+ term->disptext[idx + term->cols]);
+ term->disptext[idx + term->cols] = ldata[term->cols];
+
+ /* Do Arabic shaping and bidi. */
+ if(!term->cfg.bidi || !term->cfg.arabicshaping) {
+
+ if (!term_bidi_cache_hit(term, i, ldata, term->cols)) {
+
+ for(it=0; it<term->cols ; it++)
+ {
+ int uc = (ldata[it] & 0xFFFF);
+
+ switch (uc & CSET_MASK) {
+ case ATTR_LINEDRW:
+ if (!term->cfg.rawcnp) {
+ uc = term->ucsdata->unitab_xterm[uc & 0xFF];
+ break;
+ }
+ case ATTR_ASCII:
+ uc = term->ucsdata->unitab_line[uc & 0xFF];
+ break;
+ case ATTR_SCOACS:
+ uc = term->ucsdata->unitab_scoacs[uc&0xFF];
+ break;
+ }
+ switch (uc & CSET_MASK) {
+ case ATTR_ACP:
+ uc = term->ucsdata->unitab_font[uc & 0xFF];
+ break;
+ case ATTR_OEMCP:
+ uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
+ break;
+ }
+
+ term->wcFrom[it].origwc = term->wcFrom[it].wc = uc;
+ term->wcFrom[it].index = it;
+ }
+
+ if(!term->cfg.bidi)
+ do_bidi(term->wcFrom, term->cols);
+
+ /* this is saved iff done from inside the shaping */
+ if(!term->cfg.bidi && term->cfg.arabicshaping)
+ for(it=0; it<term->cols; it++)
+ term->wcTo[it] = term->wcFrom[it];
+
+ if(!term->cfg.arabicshaping)
+ do_shape(term->wcFrom, term->wcTo, term->cols);
+
+ for(it=0; it<term->cols ; it++)
+ {
+ term->ltemp[it] = ldata[term->wcTo[it].index];
+
+ if (term->wcTo[it].origwc != term->wcTo[it].wc)
+ term->ltemp[it] = ((term->ltemp[it] & 0xFFFF0000) |
+ term->wcTo[it].wc);
+ }
+ term_bidi_cache_store(term, i, ldata, term->ltemp, term->cols);
+ ldata = term->ltemp;
+ } else {
+ ldata = term->post_bidi_cache[i];
+ }
+ }
+
+ for (j = 0; j < term->cols; j++, idx++) {
+ unsigned long tattr, tchar;
+ unsigned long *d = ldata + j;
+ int break_run;
+ scrpos.x = j;
+
+ tchar = (*d & (CHAR_MASK | CSET_MASK));
+ tattr = (*d & (ATTR_MASK ^ CSET_MASK));
+ switch (tchar & CSET_MASK) {
+ case ATTR_ASCII:
+ tchar = term->ucsdata->unitab_line[tchar & 0xFF];
+ break;
+ case ATTR_LINEDRW:
+ tchar = term->ucsdata->unitab_xterm[tchar & 0xFF];
+ break;
+ case ATTR_SCOACS:
+ tchar = term->ucsdata->unitab_scoacs[tchar&0xFF];
+ break;
+ }
+ tattr |= (tchar & CSET_MASK);
+ tchar &= CHAR_MASK;
+ if (j < term->cols-1 &&
+ (d[1] & (CHAR_MASK | CSET_MASK)) == 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 | tattr) != (term->disptext[idx]& ~ATTR_NARROW)) {
+ if ((tattr & ATTR_WIDE) == 0 &&
+ char_width(ctx, (tchar | tattr) & 0xFFFF) == 2)
+ tattr |= ATTR_NARROW;
+ } else if (term->disptext[idx]&ATTR_NARROW)
+ tattr |= ATTR_NARROW;
+
+ /* Cursor here ? Save the 'background' */
+ if (i == our_curs_y && j == our_curs_x) {
+ cursor_background = tattr | tchar;
+ term->dispcurs = term->disptext + idx;
+ }
+
+ if ((term->disptext[idx] ^ tattr) & ATTR_WIDE)
+ dirty_line = TRUE;
+
+ break_run = (((tattr ^ attr) & term->attr_mask) ||
+ j - start >= sizeof(ch));
+
+ /* Special hack for VT100 Linedraw glyphs */
+ if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
+ && tchar <= 0xBD) break_run = TRUE;
+
+ if (!term->ucsdata->dbcs_screenfont && !dirty_line) {
+ if ((tchar | tattr) == term->disptext[idx])
+ 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, lattr);
+ updated_line = 1;
+ }
+ start = j;
+ ccount = 0;
+ attr = tattr;
+ if (term->ucsdata->dbcs_screenfont)
+ last_run_dirty = dirty_run;
+ dirty_run = dirty_line;
+ }
+
+ if ((tchar | tattr) != term->disptext[idx])
+ dirty_run = TRUE;
+ ch[ccount++] = (char) tchar;
+ term->disptext[idx] = tchar | tattr;
+
+ /* If it's a wide char step along to the next one. */
+ if (tattr & ATTR_WIDE) {
+ if (++j < term->cols) {
+ idx++;
+ 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 (term->disptext[idx] != *d)
+ dirty_run = TRUE;
+ term->disptext[idx] = *d;
+ }
+ }
+ }
+ if (dirty_run && ccount > 0) {
+ do_text(ctx, start, i, ch, ccount, attr, lattr);
+ updated_line = 1;
+ }
+
+ /* Cursor on this line ? (and changed) */
+ if (i == our_curs_y && (term->curstype != cursor || updated_line)) {
+ ch[0] = (char) (cursor_background & CHAR_MASK);
+ attr = (cursor_background & ATTR_MASK) | cursor;
+ do_cursor(ctx, our_curs_x, i, ch, 1, attr, lattr);
+ term->curstype = cursor;
+ }
+ }
+}
+
+/*
+ * Flick the switch that says if blinking things should be shown or hidden.
+ */
+
+void term_blink(Terminal *term, int flg)
+{
+ long now, blink_diff;
+
+ now = GETTICKCOUNT();
+ blink_diff = now - term->last_tblink;
+
+ /* Make sure the text blinks no more than 2Hz; we'll use 0.45 s period. */
+ if (blink_diff < 0 || blink_diff > (TICKSPERSEC * 9 / 20)) {
+ term->last_tblink = now;
+ term->tblinker = !term->tblinker;
+ }
+
+ if (flg) {
+ term->blinker = 1;
+ term->last_blink = now;
+ return;
+ }
+
+ blink_diff = now - term->last_blink;
+
+ /* Make sure the cursor blinks no faster than system blink rate */
+ if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
+ return;
+
+ term->last_blink = now;
+ term->blinker = !term->blinker;
+}
+
+/*
+ * Invalidate the whole screen so it will be repainted in full.
+ */
+void term_invalidate(Terminal *term)
+{
+ int i;
+
+ for (i = 0; i < term->rows * (term->cols + 1); i++)
+ term->disptext[i] = ATTR_INVALID;
+}
+
+/*
+ * Paint the window in response to a WM_PAINT message.
+ */
+void term_paint(Terminal *term, Context ctx,
+ int left, int top, int right, int bottom, int immediately)
+{
+ int i, j;
+ if (left < 0) left = 0;
+ if (top < 0) top = 0;
+ if (right >= term->cols) right = term->cols-1;
+ if (bottom >= term->rows) bottom = term->rows-1;
+
+ for (i = top; i <= bottom && i < term->rows; i++) {
+ if ((term->disptext[i * (term->cols + 1) + term->cols] &
+ LATTR_MODE) == LATTR_NORM)
+ for (j = left; j <= right && j < term->cols; j++)
+ term->disptext[i * (term->cols + 1) + j] = ATTR_INVALID;
+ else
+ for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++)
+ term->disptext[i * (term->cols + 1) + j] = ATTR_INVALID;
+ }
+
+ /* This should happen soon enough, also for some reason it sometimes
+ * fails to actually do anything when re-sizing ... painting the wrong
+ * window perhaps ?
+ */
+ if (immediately)
+ do_paint (term, ctx, FALSE);
+}
+
+/*
+ * Attempt to scroll the scrollback. The second parameter gives the
+ * position we want to scroll to; the first is +1 to denote that
+ * this position is relative to the beginning of the scrollback, -1
+ * to denote it is relative to the end, and 0 to denote that it is
+ * relative to the current position.
+ */
+void term_scroll(Terminal *term, int rel, int where)
+{
+ int sbtop = -sblines(term);
+#ifdef OPTIMISE_SCROLL
+ int olddisptop = term->disptop;
+ int shift;
+#endif /* OPTIMISE_SCROLL */
+
+ term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where;
+ if (term->disptop < sbtop)
+ term->disptop = sbtop;
+ if (term->disptop > 0)
+ term->disptop = 0;
+ update_sbar(term);
+#ifdef OPTIMISE_SCROLL
+ shift = (term->disptop - olddisptop);
+ if (shift < term->rows && shift > -term->rows)
+ scroll_display(term, 0, term->rows - 1, shift);
+#endif /* OPTIMISE_SCROLL */
+ term_update(term);
+}
+
+static void clipme(Terminal *term, pos top, pos bottom, int rect, int desel)
+{
+ wchar_t *workbuf;
+ wchar_t *wbptr; /* where next char goes within workbuf */
+ int old_top_x;
+ int wblen = 0; /* workbuf len */
+ int buflen; /* amount of memory allocated to workbuf */
+
+ buflen = 5120; /* Default size */
+ workbuf = snewn(buflen, wchar_t);
+ wbptr = workbuf; /* start filling here */
+ old_top_x = top.x; /* needed for rect==1 */
+
+ while (poslt(top, bottom)) {
+ int nl = FALSE;
+ unsigned long *ldata = lineptr(top.y);
+ pos nlpos;
+
+ /*
+ * nlpos will point at the maximum position on this line we
+ * should copy up to. So we start it at the end of the
+ * line...
+ */
+ nlpos.y = top.y;
+ nlpos.x = term->cols;
+
+ /*
+ * ... move it backwards if there's unused space at the end
+ * of the line (and also set `nl' if this is the case,
+ * because in normal selection mode this means we need a
+ * newline at the end)...
+ */
+ if (!(ldata[term->cols] & LATTR_WRAPPED)) {
+ while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
+ (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
+ (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
+ && poslt(top, nlpos))
+ decpos(nlpos);
+ if (poslt(nlpos, bottom))
+ nl = TRUE;
+ } else if (ldata[term->cols] & LATTR_WRAPPED2) {
+ /* Ignore the last char on the line in a WRAPPED2 line. */
+ decpos(nlpos);
+ }