⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 terminal.c

📁 putty
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * Terminal emulator.
 */

#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 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 )

char *EMPTY_WINDOW_TITLE = "";

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++;
		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -