📄 terminal.c
字号:
#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <time.h>#include <assert.h>#include "putty.h"#include "terminal.h"#define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )#define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )#define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )#define posdiff(p1,p2) ( ((p1).y - (p2).y) * (term->cols+1) + (p1).x - (p2).x )/* Product-order comparisons for rectangular block selection. */#define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )#define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )#define incpos(p) ( (p).x == term->cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )#define decpos(p) ( (p).x == 0 ? ((p).x = term->cols, (p).y--, 1) : ((p).x--, 0) )#define VT52_PLUS#define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */#define CL_VT100 0x0002 /* VT100 */#define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */#define CL_VT102 0x0008 /* VT102 */#define CL_VT220 0x0010 /* VT220 */#define CL_VT320 0x0020 /* VT320 */#define CL_VT420 0x0040 /* VT420 */#define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */#define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */#define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */#define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */#define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */#define TM_VT100 (CL_ANSIMIN|CL_VT100)#define TM_VT100AVO (TM_VT100|CL_VT100AVO)#define TM_VT102 (TM_VT100AVO|CL_VT102)#define TM_VT220 (TM_VT102|CL_VT220)#define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320)#define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI)#define TM_PUTTY (0xFFFF)#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 void swap_screen(Terminal *, int, int, int);static void update_sbar(Terminal *);static void deselect(Terminal *);static void term_print_finish(Terminal *);#ifdef OPTIMISE_SCROLLstatic 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);}/* * Diagnostic function: verify that a termline has a correct * combining character structure. * * XXX-REMOVE-BEFORE-RELEASE: This is a performance-intensive * check. Although it's currently really useful for getting all the * bugs out of the new cc stuff, it will want to be absent when we * make a proper release. */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);}/* * 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; cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */}/* * 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; cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */}/* * 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 != battr) 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); } cc_check(destline); /* XXX-REMOVE-BEFORE-RELEASE */}/* * 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; cc_check(line); /* XXX-REMOVE-BEFORE-RELEASE */}/* * 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -