+#define UPDATE_DELAY ((TICKSPERSEC+49)/50)/* ticks to defer window update */
+#define TBLINK_DELAY ((TICKSPERSEC*9+19)/20)/* ticks between text blinks*/
+#define CBLINK_DELAY (CURSORBLINK) /* ticks between cursor blinks */
+#define VBELL_DELAY (VBELL_TIMEOUT) /* visual bell timeout in ticks */
+
+#define compatibility(x) \
+ if ( ((CL_##x)&term->compatibility_level) == 0 ) { \
+ term->termstate=TOPLEVEL; \
+ break; \
+ }
+#define compatibility2(x,y) \
+ if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \
+ term->termstate=TOPLEVEL; \
+ break; \
+ }
+
+#define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 )
+
+const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+#define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t))
+const wchar_t sel_nl[] = SEL_NL;
+
+/*
+ * Fetch the character at a particular position in a line array,
+ * for purposes of `wordtype'. The reason this isn't just a simple
+ * array reference is that if the character we find is UCSWIDE,
+ * then we must look one space further to the left.
+ */
+#define UCSGET(a, x) \
+ ( (x)>0 && (a)[(x)].chr == UCSWIDE ? (a)[(x)-1].chr : (a)[(x)].chr )
+
+/*
+ * Detect the various aliases of U+0020 SPACE.
+ */
+#define IS_SPACE_CHR(chr) \
+ ((chr) == 0x20 || (DIRECT_CHAR(chr) && ((chr) & 0xFF) == 0x20))
+
+/*
+ * Spot magic CSETs.
+ */
+#define CSET_OF(chr) (DIRECT_CHAR(chr)||DIRECT_FONT(chr) ? (chr)&CSET_MASK : 0)
+
+/*
+ * Internal prototypes.
+ */
+static void resizeline(Terminal *, termline *, int);
+static termline *lineptr(Terminal *, int, int, int);
+static void unlineptr(termline *);
+static void do_paint(Terminal *, Context, int);
+static void erase_lots(Terminal *, int, int, int);
+static int find_last_nonempty_line(Terminal *, tree234 *);
+static void swap_screen(Terminal *, int, int, int);
+static void update_sbar(Terminal *);
+static void deselect(Terminal *);
+static void term_print_finish(Terminal *);
+static void scroll(Terminal *, int, int, int, int);
+#ifdef OPTIMISE_SCROLL
+static void scroll_display(Terminal *, int, int, int);
+#endif /* OPTIMISE_SCROLL */
+
+static termline *newline(Terminal *term, int cols, int bce)
+{
+ termline *line;
+ int j;
+
+ line = snew(termline);
+ line->chars = snewn(cols, termchar);
+ for (j = 0; j < cols; j++)
+ line->chars[j] = (bce ? term->erase_char : term->basic_erase_char);
+ line->cols = line->size = cols;
+ line->lattr = LATTR_NORM;
+ line->temporary = FALSE;
+ line->cc_free = 0;
+
+ return line;
+}
+
+static void freeline(termline *line)
+{
+ if (line) {
+ sfree(line->chars);
+ sfree(line);
+ }
+}
+
+static void unlineptr(termline *line)
+{
+ if (line->temporary)
+ freeline(line);
+}
+
+#ifdef TERM_CC_DIAGS
+/*
+ * Diagnostic function: verify that a termline has a correct
+ * combining character structure.
+ *
+ * This is a performance-intensive check, so it's no longer enabled
+ * by default.
+ */
+static void cc_check(termline *line)
+{
+ unsigned char *flags;
+ int i, j;
+
+ assert(line->size >= line->cols);
+
+ flags = snewn(line->size, unsigned char);
+
+ for (i = 0; i < line->size; i++)
+ flags[i] = (i < line->cols);
+
+ for (i = 0; i < line->cols; i++) {
+ j = i;
+ while (line->chars[j].cc_next) {
+ j += line->chars[j].cc_next;
+ assert(j >= line->cols && j < line->size);
+ assert(!flags[j]);
+ flags[j] = TRUE;
+ }
+ }
+
+ j = line->cc_free;
+ if (j) {
+ while (1) {
+ assert(j >= line->cols && j < line->size);
+ assert(!flags[j]);
+ flags[j] = TRUE;
+ if (line->chars[j].cc_next)
+ j += line->chars[j].cc_next;
+ else
+ break;
+ }
+ }
+
+ j = 0;
+ for (i = 0; i < line->size; i++)
+ j += (flags[i] != 0);
+
+ assert(j == line->size);
+
+ sfree(flags);
+}
+#endif
+
+/*
+ * Add a combining character to a character cell.
+ */
+static void add_cc(termline *line, int col, unsigned long chr)
+{
+ int newcc;
+
+ assert(col >= 0 && col < line->cols);
+
+ /*
+ * Start by extending the cols array if the free list is empty.
+ */
+ if (!line->cc_free) {
+ int n = line->size;
+ line->size += 16 + (line->size - line->cols) / 2;
+ line->chars = sresize(line->chars, line->size, termchar);
+ line->cc_free = n;
+ while (n < line->size) {
+ if (n+1 < line->size)
+ line->chars[n].cc_next = 1;
+ else
+ line->chars[n].cc_next = 0;
+ n++;
+ }
+ }
+
+ /*
+ * Now walk the cc list of the cell in question.
+ */
+ while (line->chars[col].cc_next)
+ col += line->chars[col].cc_next;
+
+ /*
+ * `col' now points at the last cc currently in this cell; so
+ * we simply add another one.
+ */
+ newcc = line->cc_free;
+ if (line->chars[newcc].cc_next)
+ line->cc_free = newcc + line->chars[newcc].cc_next;
+ else
+ line->cc_free = 0;
+ line->chars[newcc].cc_next = 0;
+ line->chars[newcc].chr = chr;
+ line->chars[col].cc_next = newcc - col;
+
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
+}
+
+/*
+ * Clear the combining character list in a character cell.
+ */
+static void clear_cc(termline *line, int col)
+{
+ int oldfree, origcol = col;
+
+ assert(col >= 0 && col < line->cols);
+
+ if (!line->chars[col].cc_next)
+ return; /* nothing needs doing */
+
+ oldfree = line->cc_free;
+ line->cc_free = col + line->chars[col].cc_next;
+ while (line->chars[col].cc_next)
+ col += line->chars[col].cc_next;
+ if (oldfree)
+ line->chars[col].cc_next = oldfree - col;
+ else
+ line->chars[col].cc_next = 0;
+
+ line->chars[origcol].cc_next = 0;
+
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
+}
+
+/*
+ * Compare two character cells for equality. Special case required
+ * in do_paint() where we override what we expect the chr and attr
+ * fields to be.
+ */
+static int termchars_equal_override(termchar *a, termchar *b,
+ unsigned long bchr, unsigned long battr)
+{
+ /* FULL-TERMCHAR */
+ if (a->chr != bchr)
+ return FALSE;
+ if ((a->attr &~ DATTR_MASK) != (battr &~ DATTR_MASK))
+ return FALSE;
+ while (a->cc_next || b->cc_next) {
+ if (!a->cc_next || !b->cc_next)
+ return FALSE; /* one cc-list ends, other does not */
+ a += a->cc_next;
+ b += b->cc_next;
+ if (a->chr != b->chr)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int termchars_equal(termchar *a, termchar *b)
+{
+ return termchars_equal_override(a, b, b->chr, b->attr);
+}
+
+/*
+ * Copy a character cell. (Requires a pointer to the destination
+ * termline, so as to access its free list.)
+ */
+static void copy_termchar(termline *destline, int x, termchar *src)
+{
+ clear_cc(destline, x);
+
+ destline->chars[x] = *src; /* copy everything except cc-list */
+ destline->chars[x].cc_next = 0; /* and make sure this is zero */
+
+ while (src->cc_next) {
+ src += src->cc_next;
+ add_cc(destline, x, src->chr);
+ }
+
+#ifdef TERM_CC_DIAGS
+ cc_check(destline);
+#endif
+}
+
+/*
+ * Move a character cell within its termline.
+ */
+static void move_termchar(termline *line, termchar *dest, termchar *src)
+{
+ /* First clear the cc list from the original char, just in case. */
+ clear_cc(line, dest - line->chars);
+
+ /* Move the character cell and adjust its cc_next. */
+ *dest = *src; /* copy everything except cc-list */
+ if (src->cc_next)
+ dest->cc_next = src->cc_next - (dest-src);
+
+ /* Ensure the original cell doesn't have a cc list. */
+ src->cc_next = 0;
+
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
+}
+
+/*
+ * Compress and decompress a termline into an RLE-based format for
+ * storing in scrollback. (Since scrollback almost never needs to
+ * be modified and exists in huge quantities, this is a sensible
+ * tradeoff, particularly since it allows us to continue adding
+ * features to the main termchar structure without proportionally
+ * bloating the terminal emulator's memory footprint unless those
+ * features are in constant use.)
+ */
+struct buf {
+ unsigned char *data;
+ int len, size;
+};
+static void add(struct buf *b, unsigned char c)
+{
+ if (b->len >= b->size) {
+ b->size = (b->len * 3 / 2) + 512;
+ b->data = sresize(b->data, b->size, unsigned char);
+ }
+ b->data[b->len++] = c;
+}
+static int get(struct buf *b)
+{
+ return b->data[b->len++];
+}
+static void makerle(struct buf *b, termline *ldata,
+ void (*makeliteral)(struct buf *b, termchar *c,
+ unsigned long *state))
+{
+ int hdrpos, hdrsize, n, prevlen, prevpos, thislen, thispos, prev2;
+ termchar *c = ldata->chars;
+ unsigned long state = 0, oldstate;
+
+ n = ldata->cols;
+
+ hdrpos = b->len;
+ hdrsize = 0;
+ add(b, 0);
+ prevlen = prevpos = 0;
+ prev2 = FALSE;
+
+ while (n-- > 0) {
+ thispos = b->len;
+ makeliteral(b, c++, &state);
+ thislen = b->len - thispos;
+ if (thislen == prevlen &&
+ !memcmp(b->data + prevpos, b->data + thispos, thislen)) {
+ /*
+ * This literal precisely matches the previous one.
+ * Turn it into a run if it's worthwhile.
+ *
+ * With one-byte literals, it costs us two bytes to
+ * encode a run, plus another byte to write the header
+ * to resume normal output; so a three-element run is
+ * neutral, and anything beyond that is unconditionally
+ * worthwhile. With two-byte literals or more, even a
+ * 2-run is a win.
+ */
+ if (thislen > 1 || prev2) {
+ int runpos, runlen;
+
+ /*
+ * It's worth encoding a run. Start at prevpos,
+ * unless hdrsize==0 in which case we can back up
+ * another one and start by overwriting hdrpos.
+ */
+
+ hdrsize--; /* remove the literal at prevpos */
+ if (prev2) {
+ assert(hdrsize > 0);
+ hdrsize--;
+ prevpos -= prevlen;/* and possibly another one */
+ }
+
+ if (hdrsize == 0) {
+ assert(prevpos == hdrpos + 1);
+ runpos = hdrpos;
+ b->len = prevpos+prevlen;
+ } else {
+ memmove(b->data + prevpos+1, b->data + prevpos, prevlen);
+ runpos = prevpos;
+ b->len = prevpos+prevlen+1;
+ /*
+ * Terminate the previous run of ordinary
+ * literals.
+ */
+ assert(hdrsize >= 1 && hdrsize <= 128);
+ b->data[hdrpos] = hdrsize - 1;
+ }
+
+ runlen = prev2 ? 3 : 2;
+
+ while (n > 0 && runlen < 129) {
+ int tmppos, tmplen;
+ tmppos = b->len;
+ oldstate = state;
+ makeliteral(b, c, &state);
+ tmplen = b->len - tmppos;
+ b->len = tmppos;
+ if (tmplen != thislen ||
+ memcmp(b->data + runpos+1, b->data + tmppos, tmplen)) {
+ state = oldstate;
+ break; /* run over */
+ }
+ n--, c++, runlen++;
+ }
+
+ assert(runlen >= 2 && runlen <= 129);
+ b->data[runpos] = runlen + 0x80 - 2;
+
+ hdrpos = b->len;
+ hdrsize = 0;
+ add(b, 0);
+ /* And ensure this run doesn't interfere with the next. */
+ prevlen = prevpos = 0;
+ prev2 = FALSE;
+
+ continue;
+ } else {
+ /*
+ * Just flag that the previous two literals were
+ * identical, in case we find a third identical one
+ * we want to turn into a run.
+ */
+ prev2 = TRUE;
+ prevlen = thislen;
+ prevpos = thispos;
+ }
+ } else {
+ prev2 = FALSE;
+ prevlen = thislen;
+ prevpos = thispos;
+ }
+
+ /*
+ * This character isn't (yet) part of a run. Add it to
+ * hdrsize.
+ */
+ hdrsize++;
+ if (hdrsize == 128) {
+ b->data[hdrpos] = hdrsize - 1;
+ hdrpos = b->len;
+ hdrsize = 0;
+ add(b, 0);
+ prevlen = prevpos = 0;
+ prev2 = FALSE;
+ }
+ }
+
+ /*
+ * Clean up.
+ */
+ if (hdrsize > 0) {
+ assert(hdrsize <= 128);
+ b->data[hdrpos] = hdrsize - 1;
+ } else {
+ b->len = hdrpos;
+ }
+}
+static void makeliteral_chr(struct buf *b, termchar *c, unsigned long *state)
+{
+ /*
+ * My encoding for characters is UTF-8-like, in that it stores
+ * 7-bit ASCII in one byte and uses high-bit-set bytes as
+ * introducers to indicate a longer sequence. However, it's
+ * unlike UTF-8 in that it doesn't need to be able to
+ * resynchronise, and therefore I don't want to waste two bits
+ * per byte on having recognisable continuation characters.
+ * Also I don't want to rule out the possibility that I may one
+ * day use values 0x80000000-0xFFFFFFFF for interesting
+ * purposes, so unlike UTF-8 I need a full 32-bit range.
+ * Accordingly, here is my encoding:
+ *
+ * 00000000-0000007F: 0xxxxxxx (but see below)
+ * 00000080-00003FFF: 10xxxxxx xxxxxxxx
+ * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx
+ * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
+ * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
+ *
+ * (`Z' is like `x' but is always going to be zero since the
+ * values I'm encoding don't go above 2^32. In principle the
+ * five-byte form of the encoding could extend to 2^35, and
+ * there could be six-, seven-, eight- and nine-byte forms as
+ * well to allow up to 64-bit values to be encoded. But that's
+ * completely unnecessary for these purposes!)
+ *
+ * The encoding as written above would be very simple, except
+ * that 7-bit ASCII can occur in several different ways in the
+ * terminal data; sometimes it crops up in the D800 page
+ * (CSET_ASCII) but at other times it's in the 0000 page (real
+ * Unicode). Therefore, this encoding is actually _stateful_:
+ * the one-byte encoding of 00-7F actually indicates `reuse the
+ * upper three bytes of the last character', and to encode an
+ * absolute value of 00-7F you need to use the two-byte form
+ * instead.
+ */
+ if ((c->chr & ~0x7F) == *state) {
+ add(b, (unsigned char)(c->chr & 0x7F));
+ } else if (c->chr < 0x4000) {
+ add(b, (unsigned char)(((c->chr >> 8) & 0x3F) | 0x80));
+ add(b, (unsigned char)(c->chr & 0xFF));
+ } else if (c->chr < 0x200000) {
+ add(b, (unsigned char)(((c->chr >> 16) & 0x1F) | 0xC0));
+ add(b, (unsigned char)((c->chr >> 8) & 0xFF));
+ add(b, (unsigned char)(c->chr & 0xFF));
+ } else if (c->chr < 0x10000000) {
+ add(b, (unsigned char)(((c->chr >> 24) & 0x0F) | 0xE0));
+ add(b, (unsigned char)((c->chr >> 16) & 0xFF));
+ add(b, (unsigned char)((c->chr >> 8) & 0xFF));
+ add(b, (unsigned char)(c->chr & 0xFF));
+ } else {
+ add(b, 0xF0);
+ add(b, (unsigned char)((c->chr >> 24) & 0xFF));
+ add(b, (unsigned char)((c->chr >> 16) & 0xFF));
+ add(b, (unsigned char)((c->chr >> 8) & 0xFF));
+ add(b, (unsigned char)(c->chr & 0xFF));
+ }
+ *state = c->chr & ~0xFF;
+}
+static void makeliteral_attr(struct buf *b, termchar *c, unsigned long *state)
+{
+ /*
+ * My encoding for attributes is 16-bit-granular and assumes
+ * that the top bit of the word is never required. I either
+ * store a two-byte value with the top bit clear (indicating
+ * just that value), or a four-byte value with the top bit set
+ * (indicating the same value with its top bit clear).
+ *
+ * However, first I permute the bits of the attribute value, so
+ * that the eight bits of colour (four in each of fg and bg)
+ * which are never non-zero unless xterm 256-colour mode is in
+ * use are placed higher up the word than everything else. This
+ * ensures that attribute values remain 16-bit _unless_ the
+ * user uses extended colour.
+ */
+ unsigned attr, colourbits;
+
+ attr = c->attr;
+
+ assert(ATTR_BGSHIFT > ATTR_FGSHIFT);
+
+ colourbits = (attr >> (ATTR_BGSHIFT + 4)) & 0xF;
+ colourbits <<= 4;
+ colourbits |= (attr >> (ATTR_FGSHIFT + 4)) & 0xF;
+
+ attr = (((attr >> (ATTR_BGSHIFT + 8)) << (ATTR_BGSHIFT + 4)) |
+ (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
+ attr = (((attr >> (ATTR_FGSHIFT + 8)) << (ATTR_FGSHIFT + 4)) |
+ (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
+
+ attr |= (colourbits << (32-9));
+
+ if (attr < 0x8000) {
+ add(b, (unsigned char)((attr >> 8) & 0xFF));
+ add(b, (unsigned char)(attr & 0xFF));
+ } else {
+ add(b, (unsigned char)(((attr >> 24) & 0x7F) | 0x80));
+ add(b, (unsigned char)((attr >> 16) & 0xFF));
+ add(b, (unsigned char)((attr >> 8) & 0xFF));
+ add(b, (unsigned char)(attr & 0xFF));
+ }
+}
+static void makeliteral_cc(struct buf *b, termchar *c, unsigned long *state)
+{
+ /*
+ * For combining characters, I just encode a bunch of ordinary
+ * chars using makeliteral_chr, and terminate with a \0
+ * character (which I know won't come up as a combining char
+ * itself).
+ *
+ * I don't use the stateful encoding in makeliteral_chr.
+ */
+ unsigned long zstate;
+ termchar z;
+
+ while (c->cc_next) {
+ c += c->cc_next;
+
+ assert(c->chr != 0);
+
+ zstate = 0;
+ makeliteral_chr(b, c, &zstate);
+ }
+
+ z.chr = 0;
+ zstate = 0;
+ makeliteral_chr(b, &z, &zstate);
+}
+
+static termline *decompressline(unsigned char *data, int *bytes_used);
+
+static unsigned char *compressline(termline *ldata)
+{
+ struct buf buffer = { NULL, 0, 0 }, *b = &buffer;
+
+ /*
+ * First, store the column count, 7 bits at a time, least
+ * significant `digit' first, with the high bit set on all but
+ * the last.
+ */
+ {
+ int n = ldata->cols;
+ while (n >= 128) {
+ add(b, (unsigned char)((n & 0x7F) | 0x80));
+ n >>= 7;
+ }
+ add(b, (unsigned char)(n));
+ }
+
+ /*
+ * Next store the lattrs; same principle.
+ */
+ {
+ int n = ldata->lattr;
+ while (n >= 128) {
+ add(b, (unsigned char)((n & 0x7F) | 0x80));
+ n >>= 7;
+ }
+ add(b, (unsigned char)(n));
+ }
+
+ /*
+ * Now we store a sequence of separate run-length encoded
+ * fragments, each containing exactly as many symbols as there
+ * are columns in the ldata.
+ *
+ * All of these have a common basic format:
+ *
+ * - a byte 00-7F indicates that X+1 literals follow it
+ * - a byte 80-FF indicates that a single literal follows it
+ * and expects to be repeated (X-0x80)+2 times.
+ *
+ * The format of the `literals' varies between the fragments.
+ */
+ makerle(b, ldata, makeliteral_chr);
+ makerle(b, ldata, makeliteral_attr);
+ makerle(b, ldata, makeliteral_cc);
+
+ /*
+ * Diagnostics: ensure that the compressed data really does
+ * decompress to the right thing.
+ *
+ * This is a bit performance-heavy for production code.
+ */
+#ifdef TERM_CC_DIAGS
+#ifndef CHECK_SB_COMPRESSION
+ {
+ int dused;
+ termline *dcl;
+ int i;
+
+#ifdef DIAGNOSTIC_SB_COMPRESSION
+ for (i = 0; i < b->len; i++) {
+ printf(" %02x ", b->data[i]);
+ }
+ printf("\n");
+#endif
+
+ dcl = decompressline(b->data, &dused);
+ assert(b->len == dused);
+ assert(ldata->cols == dcl->cols);
+ assert(ldata->lattr == dcl->lattr);
+ for (i = 0; i < ldata->cols; i++)
+ assert(termchars_equal(&ldata->chars[i], &dcl->chars[i]));
+
+#ifdef DIAGNOSTIC_SB_COMPRESSION
+ printf("%d cols (%d bytes) -> %d bytes (factor of %g)\n",
+ ldata->cols, 4 * ldata->cols, dused,
+ (double)dused / (4 * ldata->cols));
+#endif
+
+ freeline(dcl);
+ }
+#endif
+#endif /* TERM_CC_DIAGS */
+
+ /*
+ * Trim the allocated memory so we don't waste any, and return.
+ */
+ return sresize(b->data, b->len, unsigned char);
+}
+
+static void readrle(struct buf *b, termline *ldata,
+ void (*readliteral)(struct buf *b, termchar *c,
+ termline *ldata, unsigned long *state))
+{
+ int n = 0;
+ unsigned long state = 0;
+
+ while (n < ldata->cols) {
+ int hdr = get(b);
+
+ if (hdr >= 0x80) {
+ /* A run. */
+
+ int pos = b->len, count = hdr + 2 - 0x80;
+ while (count--) {
+ assert(n < ldata->cols);
+ b->len = pos;
+ readliteral(b, ldata->chars + n, ldata, &state);
+ n++;
+ }
+ } else {
+ /* Just a sequence of consecutive literals. */
+
+ int count = hdr + 1;
+ while (count--) {
+ assert(n < ldata->cols);
+ readliteral(b, ldata->chars + n, ldata, &state);
+ n++;
+ }
+ }
+ }
+
+ assert(n == ldata->cols);
+}
+static void readliteral_chr(struct buf *b, termchar *c, termline *ldata,
+ unsigned long *state)
+{
+ int byte;
+
+ /*
+ * 00000000-0000007F: 0xxxxxxx
+ * 00000080-00003FFF: 10xxxxxx xxxxxxxx
+ * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx
+ * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx
+ * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
+ */
+
+ byte = get(b);
+ if (byte < 0x80) {
+ c->chr = byte | *state;
+ } else if (byte < 0xC0) {
+ c->chr = (byte &~ 0xC0) << 8;
+ c->chr |= get(b);
+ } else if (byte < 0xE0) {
+ c->chr = (byte &~ 0xE0) << 16;
+ c->chr |= get(b) << 8;
+ c->chr |= get(b);
+ } else if (byte < 0xF0) {
+ c->chr = (byte &~ 0xF0) << 24;
+ c->chr |= get(b) << 16;
+ c->chr |= get(b) << 8;
+ c->chr |= get(b);
+ } else {
+ assert(byte == 0xF0);
+ c->chr = get(b) << 24;
+ c->chr |= get(b) << 16;
+ c->chr |= get(b) << 8;
+ c->chr |= get(b);
+ }
+ *state = c->chr & ~0xFF;
+}
+static void readliteral_attr(struct buf *b, termchar *c, termline *ldata,
+ unsigned long *state)
+{
+ unsigned val, attr, colourbits;
+
+ val = get(b) << 8;
+ val |= get(b);
+
+ if (val >= 0x8000) {
+ val &= ~0x8000;
+ val <<= 16;
+ val |= get(b) << 8;
+ val |= get(b);
+ }
+
+ colourbits = (val >> (32-9)) & 0xFF;
+ attr = (val & ((1<<(32-9))-1));
+
+ attr = (((attr >> (ATTR_FGSHIFT + 4)) << (ATTR_FGSHIFT + 8)) |
+ (attr & ((1 << (ATTR_FGSHIFT + 4))-1)));
+ attr = (((attr >> (ATTR_BGSHIFT + 4)) << (ATTR_BGSHIFT + 8)) |
+ (attr & ((1 << (ATTR_BGSHIFT + 4))-1)));
+
+ attr |= (colourbits >> 4) << (ATTR_BGSHIFT + 4);
+ attr |= (colourbits & 0xF) << (ATTR_FGSHIFT + 4);
+
+ c->attr = attr;
+}
+static void readliteral_cc(struct buf *b, termchar *c, termline *ldata,
+ unsigned long *state)
+{
+ termchar n;
+ unsigned long zstate;
+ int x = c - ldata->chars;
+
+ c->cc_next = 0;
+
+ while (1) {
+ zstate = 0;
+ readliteral_chr(b, &n, ldata, &zstate);
+ if (!n.chr)
+ break;
+ add_cc(ldata, x, n.chr);
+ }
+}
+
+static termline *decompressline(unsigned char *data, int *bytes_used)
+{
+ int ncols, byte, shift;
+ struct buf buffer, *b = &buffer;
+ termline *ldata;
+
+ b->data = data;
+ b->len = 0;
+
+ /*
+ * First read in the column count.
+ */
+ ncols = shift = 0;
+ do {
+ byte = get(b);
+ ncols |= (byte & 0x7F) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+
+ /*
+ * Now create the output termline.
+ */
+ ldata = snew(termline);
+ ldata->chars = snewn(ncols, termchar);
+ ldata->cols = ldata->size = ncols;
+ ldata->temporary = TRUE;
+ ldata->cc_free = 0;
+
+ /*
+ * We must set all the cc pointers in ldata->chars to 0 right
+ * now, so that cc diagnostics that verify the integrity of the
+ * whole line will make sense while we're in the middle of
+ * building it up.
+ */
+ {
+ int i;
+ for (i = 0; i < ldata->cols; i++)
+ ldata->chars[i].cc_next = 0;
+ }
+
+ /*
+ * Now read in the lattr.
+ */
+ ldata->lattr = shift = 0;
+ do {
+ byte = get(b);
+ ldata->lattr |= (byte & 0x7F) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+
+ /*
+ * Now we read in each of the RLE streams in turn.
+ */
+ readrle(b, ldata, readliteral_chr);
+ readrle(b, ldata, readliteral_attr);
+ readrle(b, ldata, readliteral_cc);
+
+ /* Return the number of bytes read, for diagnostic purposes. */
+ if (bytes_used)
+ *bytes_used = b->len;
+
+ return ldata;
+}
+
+/*
+ * Resize a line to make it `cols' columns wide.
+ */
+static void resizeline(Terminal *term, termline *line, int cols)
+{
+ int i, oldcols;
+
+ if (line->cols != cols) {
+
+ oldcols = line->cols;
+
+ /*
+ * This line is the wrong length, which probably means it
+ * hasn't been accessed since a resize. Resize it now.
+ *
+ * First, go through all the characters that will be thrown
+ * out in the resize (if we're shrinking the line) and
+ * return their cc lists to the cc free list.
+ */
+ for (i = cols; i < oldcols; i++)
+ clear_cc(line, i);
+
+ /*
+ * If we're shrinking the line, we now bodily move the
+ * entire cc section from where it started to where it now
+ * needs to be. (We have to do this before the resize, so
+ * that the data we're copying is still there. However, if
+ * we're expanding, we have to wait until _after_ the
+ * resize so that the space we're copying into is there.)
+ */
+ if (cols < oldcols)
+ memmove(line->chars + cols, line->chars + oldcols,
+ (line->size - line->cols) * TSIZE);
+
+ /*
+ * Now do the actual resize, leaving the _same_ amount of
+ * cc space as there was to begin with.
+ */
+ line->size += cols - oldcols;
+ line->chars = sresize(line->chars, line->size, TTYPE);
+ line->cols = cols;
+
+ /*
+ * If we're expanding the line, _now_ we move the cc
+ * section.
+ */
+ if (cols > oldcols)
+ memmove(line->chars + cols, line->chars + oldcols,
+ (line->size - line->cols) * TSIZE);
+
+ /*
+ * Go through what's left of the original line, and adjust
+ * the first cc_next pointer in each list. (All the
+ * subsequent ones are still valid because they are
+ * relative offsets within the cc block.) Also do the same
+ * to the head of the cc_free list.
+ */
+ for (i = 0; i < oldcols && i < cols; i++)
+ if (line->chars[i].cc_next)
+ line->chars[i].cc_next += cols - oldcols;
+ if (line->cc_free)
+ line->cc_free += cols - oldcols;
+
+ /*
+ * And finally fill in the new space with erase chars. (We
+ * don't have to worry about cc lists here, because we
+ * _know_ the erase char doesn't have one.)
+ */
+ for (i = oldcols; i < cols; i++)
+ line->chars[i] = term->basic_erase_char;
+
+#ifdef TERM_CC_DIAGS
+ cc_check(line);
+#endif
+ }
+}
+
+/*
+ * Get the number of lines in the scrollback.
+ */
+static int sblines(Terminal *term)
+{
+ int sblines = count234(term->scrollback);
+ if (term->cfg.erase_to_scrollback &&
+ term->alt_which && term->alt_screen) {
+ sblines += term->alt_sblines;
+ }
+ return sblines;
+}
+
+/*
+ * Retrieve a line of the screen or of the scrollback, according to
+ * whether the y coordinate is non-negative or negative
+ * (respectively).
+ */
+static termline *lineptr(Terminal *term, int y, int lineno, int screen)
+{
+ termline *line;
+ tree234 *whichtree;
+ int treeindex;
+
+ if (y >= 0) {
+ whichtree = term->screen;
+ treeindex = y;
+ } else {
+ int altlines = 0;
+
+ assert(!screen);
+
+ if (term->cfg.erase_to_scrollback &&
+ term->alt_which && term->alt_screen) {
+ altlines = term->alt_sblines;
+ }
+ if (y < -altlines) {
+ whichtree = term->scrollback;
+ treeindex = y + altlines + count234(term->scrollback);
+ } else {
+ whichtree = term->alt_screen;
+ treeindex = y + term->alt_sblines;
+ /* treeindex = y + count234(term->alt_screen); */
+ }