📄 terminal.c
字号:
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). */ if (c->attr < 0x8000) { add(b, (unsigned char)((c->attr >> 8) & 0xFF)); add(b, (unsigned char)(c->attr & 0xFF)); } else { add(b, (unsigned char)(((c->attr >> 24) & 0x7F) | 0x80)); add(b, (unsigned char)((c->attr >> 16) & 0xFF)); add(b, (unsigned char)((c->attr >> 8) & 0xFF)); add(b, (unsigned char)(c->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. * * XXX-REMOVE-BEFORE-RELEASE: This is a bit performance-heavy * to be leaving in production code. */#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 /* * 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){ int val; val = get(b) << 8; val |= get(b); if (val >= 0x8000) { val <<= 16; val |= get(b) << 8; val |= get(b); } c->attr = val;}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; cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */ }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -