X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/a5f3e637bc3925f7c68ab0f3895408bb70154b13..4eeb7d098fc3c6fb29a4ba0da78edbf83f88dce1:/terminal.c diff --git a/terminal.c b/terminal.c index 1fb7fced..9abb8483 100644 --- a/terminal.c +++ b/terminal.c @@ -9,6 +9,8 @@ #include "putty.h" #include "tree234.h" +#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 */ @@ -54,7 +56,8 @@ static int disptop; /* distance scrolled back (0 or -ve) */ static unsigned long *cpos; /* cursor position (convenience) */ static unsigned long *disptext; /* buffer of text on real screen */ -static unsigned long *wanttext; /* buffer of text we want on screen */ +static unsigned long *dispcurs; /* location of cursor on real screen */ +static unsigned long curstype; /* type of cursor on real screen */ #define VBELL_TIMEOUT 100 /* millisecond len of visual bell */ @@ -67,8 +70,6 @@ int nbeeps; int beep_overloaded; long lastbeep; -static unsigned char *selspace; /* buffer for building selections in */ - #define TSIZE (sizeof(unsigned long)) #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0) @@ -103,6 +104,10 @@ static int tblinker; /* When the blinking text is on */ static int blink_is_real; /* Actually blink blinking text */ static int term_echoing; /* Does terminal want local echo? */ static int term_editing; /* Does terminal want local edit? */ +static int vt52_bold; /* Force bold on non-bold colours */ +static int utf_state; /* Is there a pending UTF-8 character */ +static int utf_char; /* and what is it so far. */ +static int utf_size; /* The size of the UTF character. */ static int xterm_mouse; /* send mouse messages to app */ @@ -142,14 +147,13 @@ static enum { DO_CTRLS, - IGNORE_NEXT, - SET_GL, SET_GR, SEEN_OSC_P, OSC_STRING, OSC_MAYBE_ST, - SEEN_ESCHASH, VT52_ESC, VT52_Y1, - VT52_Y2 + VT52_Y2, + VT52_FG, + VT52_BG } termstate; static enum { @@ -179,8 +183,9 @@ static short wordness[256] = { 2, 2, 2, 2, 2, 2, 2, 2, /* EF */ }; -static unsigned char sel_nl[] = SEL_NL; -static char *paste_buffer = 0; +#define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t)) +static wchar_t sel_nl[] = SEL_NL; +static wchar_t *paste_buffer = 0; static int paste_len, paste_pos, paste_hold; /* @@ -262,6 +267,7 @@ static void power_on(void) rvideo = 0; in_vbell = FALSE; cursor_on = 1; + big_cursor = 0; save_attr = curr_attr = ATTR_DEFAULT; term_editing = term_echoing = FALSE; ldisc_send(NULL, 0); /* cause ldisc to notice changes */ @@ -336,9 +342,8 @@ void term_init(void) { screen = alt_screen = scrollback = NULL; disptop = 0; - disptext = wanttext = NULL; + disptext = dispcurs = NULL; tabs = NULL; - selspace = NULL; deselect(); rows = cols = -1; power_on(); @@ -354,7 +359,7 @@ void term_init(void) void term_size(int newrows, int newcols, int newsavelines) { tree234 *newsb, *newscreen, *newalt; - unsigned long *newdisp, *newwant, *oldline, *line; + unsigned long *newdisp, *oldline, *line; int i, j, ccols; int sblen; int save_alt_which = alt_which; @@ -426,12 +431,7 @@ void term_size(int newrows, int newcols, int newsavelines) newdisp[i] = ATTR_INVALID; sfree(disptext); disptext = newdisp; - - newwant = smalloc(newrows * (newcols + 1) * TSIZE); - for (i = 0; i < newrows * (newcols + 1); i++) - newwant[i] = ATTR_INVALID; - sfree(wanttext); - wanttext = newwant; + dispcurs = NULL; newalt = newtree234(NULL); for (i = 0; i < newrows; i++) { @@ -448,10 +448,6 @@ void term_size(int newrows, int newcols, int newsavelines) } alt_screen = newalt; - sfree(selspace); - selspace = - smalloc((newrows + newsavelines) * (newcols + sizeof(sel_nl))); - tabs = srealloc(tabs, newcols * sizeof(*tabs)); { int i; @@ -731,7 +727,7 @@ static void erase_lots(int line_only, int from_begin, int to_end) ldata = lineptr(start.y); while (poslt(start, end)) { if (start.y == cols && !erase_lattr) - ldata[start.x] &= ~ATTR_WRAPPED; + ldata[start.x] &= ~LATTR_WRAPPED; else ldata[start.x] = erase_char; if (incpos(start) && start.y < rows) @@ -784,6 +780,12 @@ static void toggle_mode(int mode, int query, int state) break; case 2: /* VT52 mode */ vt52_mode = !state; + if (vt52_mode) { + blink_is_real = FALSE; + vt52_bold = FALSE; + } else { + blink_is_real = cfg.blinktext; + } break; case 3: /* 80/132 columns */ deselect(); @@ -857,6 +859,9 @@ static void toggle_mode(int mode, int query, int state) case 20: /* Return sends ... */ cr_lf_return = state; break; + case 34: /* Make cursor BIG */ + compatibility2(OTHER, VT220); + big_cursor = !state; } } @@ -907,8 +912,149 @@ void term_out(void) * be able to display 8-bit characters, but I'll let that go 'cause * of i18n. */ - if (((c & 0x60) == 0 || c == '\177') && - termstate < DO_CTRLS && ((c & 0x80) == 0 || has_compat(VT220))) { + + /* First see about all those translations. */ + if (termstate == TOPLEVEL) { + if (utf) + switch (utf_state) { + case 0: + if (c < 0x80) { + /* I know; gotos are evil. This one is really bad! + * But before you try removing it follow the path of the + * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on. + */ + /* + if (cfg.no_vt_graph_with_utf8) break; + */ + goto evil_jump; + } else if ((c & 0xe0) == 0xc0) { + utf_size = utf_state = 1; + utf_char = (c & 0x1f); + } else if ((c & 0xf0) == 0xe0) { + utf_size = utf_state = 2; + utf_char = (c & 0x0f); + } else if ((c & 0xf8) == 0xf0) { + utf_size = utf_state = 3; + utf_char = (c & 0x07); + } else if ((c & 0xfc) == 0xf8) { + utf_size = utf_state = 4; + utf_char = (c & 0x03); + } else if ((c & 0xfe) == 0xfc) { + utf_size = utf_state = 5; + utf_char = (c & 0x01); + } else { + c = UCSERR; + break; + } + continue; + case 1: + case 2: + case 3: + case 4: + case 5: + if ((c & 0xC0) != 0x80) { + inbuf_reap--; /* This causes the faulting character */ + c = UCSERR; /* to be logged twice - not really a */ + utf_state = 0; /* serious problem. */ + break; + } + utf_char = (utf_char << 6) | (c & 0x3f); + if (--utf_state) + continue; + + c = utf_char; + + /* Is somebody trying to be evil! */ + if (c < 0x80 || + (c < 0x800 && utf_size >= 2) || + (c < 0x10000 && utf_size >= 3) || + (c < 0x200000 && utf_size >= 4) || + (c < 0x4000000 && utf_size >= 5)) + c = UCSERR; + + /* Unicode line separator and paragraph separator are CR-LF */ + if (c == 0x2028 || c == 0x2029) + c = 0x85; + + /* High controls are probably a Baaad idea too. */ + if (c < 0xA0) + c = 0xFFFD; + + /* The UTF-16 surrogates are not nice either. */ + /* The standard give the option of decoding these: + * I don't want to! */ + if (c >= 0xD800 && c < 0xE000) + c = UCSERR; + + /* ISO 10646 characters now limited to UTF-16 range. */ + if (c > 0x10FFFF) + c = UCSERR; + + /* This is currently a TagPhobic application.. */ + if (c >= 0xE0000 && c <= 0xE007F) + continue; + + /* U+FEFF is best seen as a null. */ + if (c == 0xFEFF) + continue; + /* But U+FFFE is an error. */ + if (c == 0xFFFE || c == 0xFFFF) + c = UCSERR; + + /* Oops this is a 16bit implementation */ + if (c >= 0x10000) + c = 0xFFFD; + break; + } else { + evil_jump:; + switch (cset_attr[cset]) { + /* + * Linedraw characters are different from 'ESC ( B' + * only for a small range. For ones outside that + * range, make sure we use the same font as well as + * the same encoding. + */ + case ATTR_LINEDRW: + if (unitab_ctrl[c] != 0xFF) + c = unitab_ctrl[c]; + else + c = ((unsigned char) c) | ATTR_LINEDRW; + break; + + case ATTR_GBCHR: + /* If UK-ASCII, make the '#' a LineDraw Pound */ + if (c == '#') { + c = '}' | ATTR_LINEDRW; + break; + } + /*FALLTHROUGH*/ case ATTR_ASCII: + if (unitab_ctrl[c] != 0xFF) + c = unitab_ctrl[c]; + else + c = ((unsigned char) c) | ATTR_ASCII; + break; + } + } + } + + /* How about C1 controls ? */ + if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode && + has_compat(VT220)) { + termstate = SEEN_ESC; + esc_query = FALSE; + c = '@' + (c & 0x1F); + } + + /* Or the GL control. */ + if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) { + if (curs.x && !wrapnext) + curs.x--; + wrapnext = FALSE; + fix_cpos; + *cpos = (' ' | curr_attr | ATTR_ASCII); + } else + /* Or normal C0 controls. */ + if ((c & -32) == 0 && termstate < DO_CTRLS) { switch (c) { case '\005': /* terminal type query */ /* Strictly speaking this is VT100 but a VT100 defaults to @@ -936,9 +1082,9 @@ void term_out(void) } else if (*s == '^') { state = 1; } else - *d++ = xlat_kbd2tty((unsigned char) *s); + *d++ = *s; } - ldisc_send(abuf, d - abuf); + lpage_send(CP_ACP, abuf, d - abuf); } break; case '\007': @@ -1034,20 +1180,9 @@ void term_out(void) else { compatibility(ANSIMIN); termstate = SEEN_ESC; + esc_query = FALSE; } break; - case 0233: - compatibility(VT220); - termstate = SEEN_CSI; - esc_nargs = 1; - esc_args[0] = ARG_DEFAULT; - esc_query = FALSE; - break; - case 0235: - compatibility(VT220); - termstate = SEEN_OSC; - esc_args[0] = 0; - break; case '\r': curs.x = 0; wrapnext = FALSE; @@ -1102,22 +1237,13 @@ void term_out(void) } seen_disp_event = TRUE; break; - case '\177': /* Destructive backspace - This does nothing on a real VT100 */ - compatibility(OTHER); - if (curs.x && !wrapnext) - curs.x--; - wrapnext = FALSE; - fix_cpos; - *cpos = (' ' | curr_attr | ATTR_ASCII); - break; } } else switch (termstate) { case TOPLEVEL: /* Only graphic characters get this far, ctrls are stripped above */ if (wrapnext && wrap) { - cpos[1] |= ATTR_WRAPPED; + cpos[1] |= LATTR_WRAPPED; if (curs.y == marg_b) scroll(marg_t, marg_b, 1, TRUE); else if (curs.y < rows - 1) @@ -1133,49 +1259,49 @@ void term_out(void) incpos(cursplus); check_selection(curs, cursplus); } - switch (cset_attr[cset]) { - /* - * Linedraw characters are different from 'ESC ( B' - * only for a small range. For ones outside that - * range, make sure we use the same font as well as - * the same encoding. - */ - case ATTR_LINEDRW: - if (c < 0x5f || c > 0x7F) - *cpos++ = - xlat_tty2scr((unsigned char) c) | curr_attr | - ATTR_ASCII; - else if (c == 0x5F) - *cpos++ = ' ' | curr_attr | ATTR_ASCII; - else - *cpos++ = - ((unsigned char) c) | curr_attr | ATTR_LINEDRW; - break; - case ATTR_GBCHR: - /* If UK-ASCII, make the '#' a LineDraw Pound */ - if (c == '#') { - *cpos++ = '}' | curr_attr | ATTR_LINEDRW; + if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0) + logtraffic((unsigned char) c, LGTYP_ASCII); + { + extern int wcwidth(wchar_t ucs); + int width = 0; + if (DIRECT_CHAR(c)) + width = 1; + if (!width) + width = wcwidth((wchar_t) c); + switch (width) { + case 2: + if (curs.x + 1 != cols) { + *cpos++ = c | ATTR_WIDE | curr_attr; + *cpos++ = UCSWIDE | curr_attr; + curs.x++; + break; + } + case 1: + *cpos++ = c | curr_attr; break; + default: + continue; } - /*FALLTHROUGH*/ default: - *cpos = xlat_tty2scr((unsigned char) c) | curr_attr | - (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII); - logtraffic((unsigned char) c, LGTYP_ASCII); - cpos++; - break; } curs.x++; if (curs.x == cols) { cpos--; curs.x--; wrapnext = TRUE; + if (wrap && vt52_mode) { + cpos[1] |= LATTR_WRAPPED; + if (curs.y == marg_b) + scroll(marg_t, marg_b, 1, TRUE); + else if (curs.y < rows - 1) + curs.y++; + curs.x = 0; + fix_cpos; + wrapnext = FALSE; + } } seen_disp_event = 1; break; - case IGNORE_NEXT: - termstate = TOPLEVEL; - break; case OSC_MAYBE_ST: /* * This state is virtually identical to SEEN_ESC, with the @@ -1189,12 +1315,15 @@ void term_out(void) } /* else fall through */ case SEEN_ESC: - termstate = TOPLEVEL; - switch (c) { - case ' ': /* some weird sequence? */ - compatibility(VT220); - termstate = IGNORE_NEXT; + if (c >= ' ' && c <= '/') { + if (esc_query) + esc_query = -1; + else + esc_query = c; break; + } + termstate = TOPLEVEL; + switch (ANSI(c, esc_query)) { case '[': /* enter CSI mode */ termstate = SEEN_CSI; esc_nargs = 1; @@ -1207,14 +1336,6 @@ void term_out(void) termstate = SEEN_OSC; esc_args[0] = 0; break; - case '(': /* should set GL */ - compatibility(VT100); - termstate = SET_GL; - break; - case ')': /* should set GR */ - compatibility(VT100); - termstate = SET_GR; - break; case '7': /* save cursor */ compatibility(VT100); save_cursor(TRUE); @@ -1278,14 +1399,97 @@ void term_out(void) disptop = 0; seen_disp_event = TRUE; break; - case '#': /* ESC # 8 fills screen with Es :-) */ - compatibility(VT100); - termstate = SEEN_ESCHASH; - break; case 'H': /* set a tab */ compatibility(VT100); tabs[curs.x] = TRUE; break; + + case ANSI('8', '#'): /* ESC # 8 fills screen with Es :-) */ + compatibility(VT100); + { + unsigned long *ldata; + int i, j; + pos scrtop, scrbot; + + for (i = 0; i < rows; i++) { + ldata = lineptr(i); + for (j = 0; j < cols; j++) + ldata[j] = ATTR_DEFAULT | 'E'; + ldata[cols] = 0; + } + disptop = 0; + seen_disp_event = TRUE; + scrtop.x = scrtop.y = 0; + scrbot.x = 0; + scrbot.y = rows; + check_selection(scrtop, scrbot); + } + break; + + case ANSI('3', '#'): + case ANSI('4', '#'): + case ANSI('5', '#'): + case ANSI('6', '#'): + compatibility(VT100); + { + unsigned long nlattr; + unsigned long *ldata; + switch (ANSI(c, esc_query)) { + case ANSI('3', '#'): + nlattr = LATTR_TOP; + break; + case ANSI('4', '#'): + nlattr = LATTR_BOT; + break; + case ANSI('5', '#'): + nlattr = LATTR_NORM; + break; + case ANSI('6', '#'): + nlattr = LATTR_WIDE; + break; + } + ldata = lineptr(curs.y); + ldata[cols] &= ~LATTR_MODE; + ldata[cols] |= nlattr; + } + break; + + case ANSI('A', '('): + compatibility(VT100); + cset_attr[0] = ATTR_GBCHR; + break; + case ANSI('B', '('): + compatibility(VT100); + cset_attr[0] = ATTR_ASCII; + break; + case ANSI('0', '('): + compatibility(VT100); + cset_attr[0] = ATTR_LINEDRW; + break; + + case ANSI('A', ')'): + compatibility(VT100); + cset_attr[1] = ATTR_GBCHR; + break; + case ANSI('B', ')'): + compatibility(VT100); + cset_attr[1] = ATTR_ASCII; + break; + case ANSI('0', ')'): + compatibility(VT100); + cset_attr[1] = ATTR_LINEDRW; + break; + + case ANSI('8', '%'): /* Old Linux code */ + case ANSI('G', '%'): + compatibility(OTHER); + utf = 1; + break; + case ANSI('@', '%'): + compatibility(OTHER); + if (line_codepage != CP_UTF8) + utf = 0; + break; } break; case SEEN_CSI: @@ -1502,11 +1706,9 @@ void term_out(void) * this was selected by CSI 7m. * * case 2: - * This is DIM on the VT100-AVO and VT102 - * case 5: - * This is BLINK on the VT100-AVO and VT102+ + * This is sometimes DIM, eg on the GIGI and Linux * case 8: - * This is INVIS on the VT100-AVO and VT102 + * This is sometimes INVIS various ANSI. * case 21: * This like 22 disables BOLD, DIM and INVIS * @@ -1711,7 +1913,7 @@ void term_out(void) * This first appeared in the VT220, but we do need to get * back to PuTTY mode so I won't check it. * - * The arg in 40..42 are a PuTTY extension. + * The arg in 40..42,50 are a PuTTY extension. * The 2nd arg, 8bit vs 7bit is not checked. * * Setting VT102 mode should also change the Fkeys to @@ -1781,24 +1983,6 @@ void term_out(void) break; } break; - case SET_GL: - case SET_GR: - /* VT100 only here, checked above */ - switch (c) { - case 'A': - cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR; - break; - case '0': - cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW; - break; - case 'B': - default: /* specifically, 'B' */ - cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII; - break; - } - if (!has_compat(VT220) || c != '%') - termstate = TOPLEVEL; - break; case SEEN_OSC: osc_w = FALSE; switch (c) { @@ -1912,55 +2096,6 @@ void term_out(void) osc_strlen = 0; } break; - case SEEN_ESCHASH: - { - unsigned long nlattr; - unsigned long *ldata; - int i, j; - pos scrtop, scrbot; - - switch (c) { - case '8': - for (i = 0; i < rows; i++) { - ldata = lineptr(i); - for (j = 0; j < cols; j++) - ldata[j] = ATTR_DEFAULT | 'E'; - ldata[cols] = 0; - } - disptop = 0; - seen_disp_event = TRUE; - scrtop.x = scrtop.y = 0; - scrbot.x = 0; - scrbot.y = rows; - check_selection(scrtop, scrbot); - break; - - case '3': - case '4': - case '5': - case '6': - switch (c) { - case '3': - nlattr = LATTR_TOP; - break; - case '4': - nlattr = LATTR_BOT; - break; - case '5': - nlattr = LATTR_NORM; - break; - case '6': - nlattr = LATTR_WIDE; - break; - } - - ldata = lineptr(curs.y); - ldata[cols] &= ~LATTR_MODE; - ldata[cols] |= nlattr; - } - } - termstate = TOPLEVEL; - break; case VT52_ESC: termstate = TOPLEVEL; seen_disp_event = TRUE; @@ -1977,6 +2112,46 @@ void term_out(void) case 'D': move(curs.x - 1, curs.y, 1); break; + /* + * From the VT100 Manual + * NOTE: The special graphics characters in the VT100 + * are different from those in the VT52 + * + * From VT102 manual: + * 137 _ Blank - Same + * 140 ` Reserved - Humm. + * 141 a Solid rectangle - Similar + * 142 b 1/ - Top half of fraction for the + * 143 c 3/ - subscript numbers below. + * 144 d 5/ + * 145 e 7/ + * 146 f Degrees - Same + * 147 g Plus or minus - Same + * 150 h Right arrow + * 151 i Ellipsis (dots) + * 152 j Divide by + * 153 k Down arrow + * 154 l Bar at scan 0 + * 155 m Bar at scan 1 + * 156 n Bar at scan 2 + * 157 o Bar at scan 3 - Similar + * 160 p Bar at scan 4 - Similar + * 161 q Bar at scan 5 - Similar + * 162 r Bar at scan 6 - Same + * 163 s Bar at scan 7 - Similar + * 164 t Subscript 0 + * 165 u Subscript 1 + * 166 v Subscript 2 + * 167 w Subscript 3 + * 170 x Subscript 4 + * 171 y Subscript 5 + * 172 z Subscript 6 + * 173 { Subscript 7 + * 174 | Subscript 8 + * 175 } Subscript 9 + * 176 ~ Paragraph + * + */ case 'F': cset_attr[cset = 0] = ATTR_LINEDRW; break; @@ -2001,6 +2176,7 @@ void term_out(void) case 'K': erase_lots(TRUE, FALSE, TRUE); break; +#if 0 case 'V': /* XXX Print cursor line */ break; @@ -2010,6 +2186,7 @@ void term_out(void) case 'X': /* XXX Stop controller mode */ break; +#endif case 'Y': termstate = VT52_Y1; break; @@ -2028,7 +2205,9 @@ void term_out(void) * emulation. */ vt52_mode = FALSE; + blink_is_real = cfg.blinktext; break; +#if 0 case '^': /* XXX Enter auto print mode */ break; @@ -2038,6 +2217,105 @@ void term_out(void) case ']': /* XXX Print screen */ break; +#endif + +#ifdef VT52_PLUS + case 'E': + /* compatibility(ATARI) */ + move(0, 0, 0); + erase_lots(FALSE, FALSE, TRUE); + disptop = 0; + break; + case 'L': + /* compatibility(ATARI) */ + if (curs.y <= marg_b) + scroll(curs.y, marg_b, -1, FALSE); + break; + case 'M': + /* compatibility(ATARI) */ + if (curs.y <= marg_b) + scroll(curs.y, marg_b, 1, TRUE); + break; + case 'b': + /* compatibility(ATARI) */ + termstate = VT52_FG; + break; + case 'c': + /* compatibility(ATARI) */ + termstate = VT52_BG; + break; + case 'd': + /* compatibility(ATARI) */ + erase_lots(FALSE, TRUE, FALSE); + disptop = 0; + break; + case 'e': + /* compatibility(ATARI) */ + cursor_on = TRUE; + break; + case 'f': + /* compatibility(ATARI) */ + cursor_on = FALSE; + break; + /* case 'j': Save cursor position - broken on ST */ + /* case 'k': Restore cursor position */ + case 'l': + /* compatibility(ATARI) */ + erase_lots(TRUE, TRUE, TRUE); + curs.x = 0; + wrapnext = FALSE; + fix_cpos; + break; + case 'o': + /* compatibility(ATARI) */ + erase_lots(TRUE, TRUE, FALSE); + break; + case 'p': + /* compatibility(ATARI) */ + curr_attr |= ATTR_REVERSE; + break; + case 'q': + /* compatibility(ATARI) */ + curr_attr &= ~ATTR_REVERSE; + break; + case 'v': /* wrap Autowrap on - Wyse style */ + /* compatibility(ATARI) */ + wrap = 1; + break; + case 'w': /* Autowrap off */ + /* compatibility(ATARI) */ + wrap = 0; + break; + + case 'R': + /* compatibility(OTHER) */ + vt52_bold = FALSE; + curr_attr = ATTR_DEFAULT; + if (use_bce) + erase_char = (' ' | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK | + ATTR_BLINK))); + break; + case 'S': + /* compatibility(VI50) */ + curr_attr |= ATTR_UNDER; + break; + case 'W': + /* compatibility(VI50) */ + curr_attr &= ~ATTR_UNDER; + break; + case 'U': + /* compatibility(VI50) */ + vt52_bold = TRUE; + curr_attr |= ATTR_BOLD; + break; + case 'T': + /* compatibility(VI50) */ + vt52_bold = FALSE; + curr_attr &= ~ATTR_BOLD; + break; +#endif } break; case VT52_Y1: @@ -2048,6 +2326,39 @@ void term_out(void) termstate = TOPLEVEL; move(c - ' ', curs.y, 0); break; + +#ifdef VT52_PLUS + case VT52_FG: + termstate = TOPLEVEL; + curr_attr &= ~ATTR_FGMASK; + curr_attr &= ~ATTR_BOLD; + curr_attr |= (c & 0x7) << ATTR_FGSHIFT; + if ((c & 0x8) || vt52_bold) + curr_attr |= ATTR_BOLD; + + if (use_bce) + erase_char = (' ' | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK | + ATTR_BLINK))); + break; + case VT52_BG: + termstate = TOPLEVEL; + curr_attr &= ~ATTR_BGMASK; + curr_attr &= ~ATTR_BLINK; + curr_attr |= (c & 0x7) << ATTR_BGSHIFT; + + /* Note: bold background */ + if (c & 0x8) + curr_attr |= ATTR_BLINK; + + if (use_bce) + erase_char = (' ' | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK | + ATTR_BLINK))); + break; +#endif } if (selstate != NO_SELECTION) { pos cursplus = curs; @@ -2058,6 +2369,7 @@ void term_out(void) inbuf_head = 0; } +#if 0 /* * Compare two lines to determine whether they are sufficiently * alike to scroll-optimise one to the other. Return the degree of @@ -2071,6 +2383,7 @@ static int linecmp(unsigned long *a, unsigned long *b) n += (*a++ == *b++); return n; } +#endif /* * Given a context, update the window. Out of paranoia, we don't @@ -2078,10 +2391,11 @@ static int linecmp(unsigned long *a, unsigned long *b) */ static void do_paint(Context ctx, int may_optimise) { - int i, j, start, our_curs_y; - unsigned long attr, rv, cursor; + int i, j, our_curs_y; + unsigned long rv, cursor; pos scrpos; char ch[1024]; + long cursor_background = ERASE_CHAR; long ticks; /* @@ -2093,83 +2407,162 @@ static void do_paint(Context ctx, int may_optimise) in_vbell = FALSE; } + rv = (!rvideo ^ !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 + * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext */ + + /* Has the cursor position or type changed ? */ if (cursor_on) { if (has_focus) { if (blinker || !cfg.blink_cur) - cursor = ATTR_ACTCURS; + cursor = TATTR_ACTCURS; else cursor = 0; } else - cursor = ATTR_PASCURS; + cursor = TATTR_PASCURS; if (wrapnext) - cursor |= ATTR_RIGHTCURS; + cursor |= TATTR_RIGHTCURS; } else cursor = 0; - rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0); our_curs_y = curs.y - disptop; + if (dispcurs && (curstype != cursor || + dispcurs != + disptext + our_curs_y * (cols + 1) + curs.x)) { + if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE)) + dispcurs[-1] |= ATTR_INVALID; + if ((*dispcurs & ATTR_WIDE)) + dispcurs[1] |= ATTR_INVALID; + *dispcurs |= ATTR_INVALID; + curstype = 0; + } + dispcurs = NULL; + + /* The normal screen data */ for (i = 0; i < rows; i++) { unsigned long *ldata; int lattr; + int idx, dirty_line, dirty_run; + unsigned long attr = 0; + int updated_line = 0; + int start = 0; + int ccount = 0; + int last_run_dirty = 0; + scrpos.y = i + disptop; ldata = lineptr(scrpos.y); lattr = (ldata[cols] & LATTR_MODE); - for (j = 0; j <= cols; j++) { - unsigned long d = ldata[j]; - int idx = i * (cols + 1) + j; + + idx = i * (cols + 1); + dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]); + disptext[idx + cols] = ldata[cols]; + + for (j = 0; j < cols; j++, idx++) { + unsigned long tattr, tchar; + unsigned long *d = ldata + j; + int break_run; scrpos.x = j; - wanttext[idx] = lattr | (((d & ~ATTR_WRAPPED) ^ rv - ^ (posle(selstart, scrpos) && - poslt(scrpos, selend) ? - ATTR_REVERSE : 0)) | - (i == our_curs_y - && j == curs.x ? cursor : 0)); - if (blink_is_real) { - if (has_focus && tblinker && (wanttext[idx] & ATTR_BLINK)) { - wanttext[idx] &= ATTR_MASK; - wanttext[idx] += ' '; + tchar = (*d & (CHAR_MASK | CSET_MASK)); + tattr = (*d & (ATTR_MASK ^ CSET_MASK)); + switch (tchar & CSET_MASK) { + case ATTR_ASCII: + tchar = unitab_line[tchar & 0xFF]; + break; + case ATTR_LINEDRW: + tchar = unitab_xterm[tchar & 0xFF]; + break; + } + tattr |= (tchar & CSET_MASK); + tchar &= CHAR_MASK; + + /* Video reversing things */ + tattr = (tattr ^ rv + ^ (posle(selstart, scrpos) && + poslt(scrpos, selend) ? ATTR_REVERSE : 0)); + + /* 'Real' blinking ? */ + if (blink_is_real && (tattr & ATTR_BLINK)) { + if (has_focus && tblinker) { + tchar = ' '; + tattr &= ~CSET_MASK; + tattr |= ATTR_ACP; } - wanttext[idx] &= ~ATTR_BLINK; + tattr &= ~ATTR_BLINK; } - } - } - /* - * We would perform scrolling optimisations in here, if they - * didn't have a nasty tendency to cause the whole sodding - * program to hang for a second at speed-critical moments. - * We'll leave it well alone... - */ + /* Cursor here ? Save the 'background' */ + if (i == our_curs_y && j == curs.x) { + cursor_background = tattr | tchar; + dispcurs = disptext + idx; + } - for (i = 0; i < rows; i++) { - int idx = i * (cols + 1); - int lattr = (wanttext[idx + cols] & LATTR_MODE); - start = -1; - for (j = 0; j <= cols; j++, idx++) { - unsigned long t = wanttext[idx]; - int needs_update = (j < cols && t != disptext[idx]); - int keep_going = (start != -1 && needs_update && - (t & ATTR_MASK) == attr && - j - start < sizeof(ch)); - if (start != -1 && !keep_going) { - do_text(ctx, start, i, ch, j - start, attr, lattr); - start = -1; + if ((disptext[idx] ^ tattr) & ATTR_WIDE) + dirty_line = TRUE; + + break_run = (tattr != attr || j - start >= sizeof(ch)); + + /* Special hack for VT100 Linedraw glyphs */ + if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA + && tchar <= 0xBD) break_run = TRUE; + + if (!dbcs_screenfont && !dirty_line) { + if ((tchar | tattr) == disptext[idx]) + break_run = TRUE; + else if (!dirty_run && ccount == 1) + break_run = TRUE; } - if (needs_update) { - if (start == -1) { - start = j; - attr = t & ATTR_MASK; + + 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 (dbcs_screenfont) + last_run_dirty = dirty_run; + dirty_run = dirty_line; + } + + if ((tchar | tattr) != disptext[idx]) + dirty_run = TRUE; + ch[ccount++] = (char) tchar; + disptext[idx] = tchar | tattr; + + /* If it's a wide char step along to the next one. */ + if (tattr & ATTR_WIDE) { + if (++j < cols) { + idx++; + d++; + /* Cursor is here ? Ouch! */ + if (i == our_curs_y && j == curs.x) { + cursor_background = *d; + dispcurs = disptext + idx; + } + if (disptext[idx] != *d) + dirty_run = TRUE; + disptext[idx] = *d; } - ch[j - start] = (char) (t & CHAR_MASK); } - disptext[idx] = t; + } + 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 && (curstype != cursor || updated_line)) { + ch[0] = (char) (cursor_background & CHAR_MASK); + attr = (cursor_background & ATTR_MASK) | cursor; + do_cursor(ctx, curs.x, i, ch, 1, attr, lattr); + curstype = cursor; } } } @@ -2267,17 +2660,16 @@ void term_scroll(int rel, int where) term_update(); } -static void clipme(pos top, pos bottom, char *workbuf) +static void clipme(pos top, pos bottom) { - char *wbptr; /* where next char goes within workbuf */ + wchar_t *workbuf; + wchar_t *wbptr; /* where next char goes within workbuf */ int wblen = 0; /* workbuf len */ int buflen; /* amount of memory allocated to workbuf */ - if (workbuf != NULL) { /* user supplied buffer? */ - buflen = -1; /* assume buffer passed in is big enough */ - wbptr = workbuf; /* start filling here */ - } else - buflen = 0; /* No data is available yet */ + buflen = 5120; /* Default size */ + workbuf = smalloc(buflen * sizeof(wchar_t)); + wbptr = workbuf; /* start filling here */ while (poslt(top, bottom)) { int nl = FALSE; @@ -2287,49 +2679,94 @@ static void clipme(pos top, pos bottom, char *workbuf) nlpos.y = top.y; nlpos.x = cols; - if (!(ldata[cols] & ATTR_WRAPPED)) { - while ((ldata[nlpos.x - 1] & CHAR_MASK) == 0x20 - && poslt(top, nlpos)) decpos(nlpos); + if (!(ldata[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; } while (poslt(top, bottom) && poslt(top, nlpos)) { - int ch = (ldata[top.x] & CHAR_MASK); - int set = (ldata[top.x] & CSET_MASK); - - /* VT Specials -> ISO8859-1 for Cut&Paste */ - static const unsigned char poorman2[] = - "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 "; - - if (set && !cfg.rawcnp) { - if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) { - int x; - if ((x = poorman2[2 * (ch - 0x60) + 1]) == ' ') - x = 0; - ch = (x << 8) + poorman2[2 * (ch - 0x60)]; +#if 0 + char cbuf[16], *p; + sprintf(cbuf, "", (ldata[top.x] & 0xFFFF)); +#else + wchar_t cbuf[16], *p; + int uc = (ldata[top.x] & 0xFFFF); + int set, c; + + if (uc == UCSWIDE) { + top.x++; + continue; + } + + switch (uc & CSET_MASK) { + case ATTR_LINEDRW: + if (!cfg.rawcnp) { + uc = unitab_xterm[uc & 0xFF]; + break; } + case ATTR_ASCII: + uc = unitab_line[uc & 0xFF]; + break; } + switch (uc & CSET_MASK) { + case ATTR_ACP: + uc = unitab_font[uc & 0xFF]; + break; + case ATTR_OEMCP: + uc = unitab_oemcp[uc & 0xFF]; + break; + } + + set = (uc & CSET_MASK); + c = (uc & CHAR_MASK); + cbuf[0] = uc; + cbuf[1] = 0; + + if (DIRECT_FONT(uc)) { + if (c >= ' ' && c != 0x7F) { + unsigned char buf[4]; + WCHAR wbuf[4]; + int rv; + if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) { + buf[0] = c; + buf[1] = (unsigned char) ldata[top.x + 1]; + rv = MultiByteToWideChar(font_codepage, + 0, buf, 2, wbuf, 4); + top.x++; + } else { + buf[0] = c; + rv = MultiByteToWideChar(font_codepage, + 0, buf, 1, wbuf, 4); + } - while (ch != 0) { - if (cfg.rawcnp || !!(ch & 0xE0)) { - if (wblen == buflen) { - workbuf = srealloc(workbuf, buflen += 100); - wbptr = workbuf + wblen; + if (rv > 0) { + memcpy(cbuf, wbuf, rv * sizeof(wchar_t)); + cbuf[rv] = 0; } - wblen++; - *wbptr++ = (unsigned char) ch; } - ch >>= 8; + } +#endif + + for (p = cbuf; *p; p++) { + /* Enough overhead for trailing NL and nul */ + if (wblen >= buflen - 16) { + workbuf = + srealloc(workbuf, + sizeof(wchar_t) * (buflen += 100)); + wbptr = workbuf + wblen; + } + wblen++; + *wbptr++ = *p; } top.x++; } if (nl) { int i; - for (i = 0; i < sizeof(sel_nl); i++) { - if (wblen == buflen) { - workbuf = srealloc(workbuf, buflen += 100); - wbptr = workbuf + wblen; - } + for (i = 0; i < sel_nl_sz; i++) { wblen++; *wbptr++ = sel_nl[i]; } @@ -2337,17 +2774,122 @@ static void clipme(pos top, pos bottom, char *workbuf) top.y++; top.x = 0; } + wblen++; + *wbptr++ = 0; write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */ if (buflen > 0) /* indicates we allocated this buffer */ sfree(workbuf); - } + void term_copyall(void) { pos top; top.y = -count234(scrollback); top.x = 0; - clipme(top, curs, NULL /* dynamic allocation */ ); + clipme(top, curs); +} + +/* + * The wordness array is mainly for deciding the disposition of the US-ASCII + * characters. + */ +static int wordtype(int uc) +{ + static struct { + int start, end, ctype; + } *wptr, ucs_words[] = { + { + 128, 160, 0}, { + 161, 191, 1}, { + 215, 215, 1}, { + 247, 247, 1}, { + 0x037e, 0x037e, 1}, /* Greek question mark */ + { + 0x0387, 0x0387, 1}, /* Greek ano teleia */ + { + 0x055a, 0x055f, 1}, /* Armenian punctuation */ + { + 0x0589, 0x0589, 1}, /* Armenian full stop */ + { + 0x0700, 0x070d, 1}, /* Syriac punctuation */ + { + 0x104a, 0x104f, 1}, /* Myanmar punctuation */ + { + 0x10fb, 0x10fb, 1}, /* Georgian punctuation */ + { + 0x1361, 0x1368, 1}, /* Ethiopic punctuation */ + { + 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */ + { + 0x17d4, 0x17dc, 1}, /* Khmer punctuation */ + { + 0x1800, 0x180a, 1}, /* Mongolian punctuation */ + { + 0x2000, 0x200a, 0}, /* Various spaces */ + { + 0x2070, 0x207f, 2}, /* superscript */ + { + 0x2080, 0x208f, 2}, /* subscript */ + { + 0x200b, 0x27ff, 1}, /* punctuation and symbols */ + { + 0x3000, 0x3000, 0}, /* ideographic space */ + { + 0x3001, 0x3020, 1}, /* ideographic punctuation */ + { + 0x303f, 0x309f, 3}, /* Hiragana */ + { + 0x30a0, 0x30ff, 3}, /* Katakana */ + { + 0x3300, 0x9fff, 3}, /* CJK Ideographs */ + { + 0xac00, 0xd7a3, 3}, /* Hangul Syllables */ + { + 0xf900, 0xfaff, 3}, /* CJK Ideographs */ + { + 0xfe30, 0xfe6b, 1}, /* punctuation forms */ + { + 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */ + { + 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */ + { + 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */ + { + 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */ + { + 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */ + { + 0, 0, 0} + }; + + uc &= (CSET_MASK | CHAR_MASK); + + switch (uc & CSET_MASK) { + case ATTR_LINEDRW: + uc = unitab_xterm[uc & 0xFF]; + break; + case ATTR_ASCII: + uc = unitab_line[uc & 0xFF]; + break; + } + switch (uc & CSET_MASK) { + case ATTR_ACP: + uc = unitab_font[uc & 0xFF]; + break; + case ATTR_OEMCP: + uc = unitab_oemcp[uc & 0xFF]; + break; + } + + if (uc < 0x80) + return wordness[uc]; + + for (wptr = ucs_words; wptr->start; wptr++) { + if (uc >= wptr->start && uc <= wptr->end) + return wptr->ctype; + } + + return 2; } /* @@ -2366,7 +2908,7 @@ static pos sel_spread_half(pos p, int dir) * In this mode, every character is a separate unit, except * for runs of spaces at the end of a non-wrapping line. */ - if (!(ldata[cols] & ATTR_WRAPPED)) { + if (!(ldata[cols] & LATTR_WRAPPED)) { unsigned long *q = ldata + cols; while (q > ldata && (q[-1] & CHAR_MASK) == 0x20) q--; @@ -2381,15 +2923,13 @@ static pos sel_spread_half(pos p, int dir) * In this mode, the units are maximal runs of characters * whose `wordness' has the same value. */ - wvalue = wordness[ldata[p.x] & CHAR_MASK]; + wvalue = wordtype(ldata[p.x]); if (dir == +1) { - while (p.x < cols - && wordness[ldata[p.x + 1] & CHAR_MASK] == - wvalue) p.x++; + while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue) + p.x++; } else { - while (p.x > 0 - && wordness[ldata[p.x - 1] & CHAR_MASK] == - wvalue) p.x--; + while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue) + p.x--; } break; case SM_LINE: @@ -2534,51 +3074,49 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, * We've completed a selection. We now transfer the * data to the clipboard. */ - clipme(selstart, selend, selspace); + clipme(selstart, selend); selstate = SELECTED; } else selstate = NO_SELECTION; } else if (b == MBT_PASTE && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) { - char *data; + wchar_t *data; int len; - get_clip((void **) &data, &len); + get_clip(&data, &len); if (data) { - char *p, *q; + wchar_t *p, *q; if (paste_buffer) sfree(paste_buffer); paste_pos = paste_hold = paste_len = 0; - paste_buffer = smalloc(len); + paste_buffer = smalloc(len * sizeof(wchar_t)); p = q = data; while (p < data + len) { while (p < data + len && - !(p <= data + len - sizeof(sel_nl) && + !(p <= data + len - sel_nl_sz && !memcmp(p, sel_nl, sizeof(sel_nl)))) p++; { int i; - unsigned char c; for (i = 0; i < p - q; i++) { - c = xlat_kbd2tty(q[i]); - paste_buffer[paste_len++] = c; + paste_buffer[paste_len++] = q[i]; } } - if (p <= data + len - sizeof(sel_nl) && + if (p <= data + len - sel_nl_sz && !memcmp(p, sel_nl, sizeof(sel_nl))) { paste_buffer[paste_len++] = '\r'; - p += sizeof(sel_nl); + p += sel_nl_sz; } q = p; } /* Assume a small paste will be OK in one go. */ if (paste_len < 256) { - ldisc_send(paste_buffer, paste_len); + luni_send(paste_buffer, paste_len); if (paste_buffer) sfree(paste_buffer); paste_buffer = 0; @@ -2623,7 +3161,7 @@ void term_paste() if (paste_buffer[paste_pos + n++] == '\r') break; } - ldisc_send(paste_buffer + paste_pos, n); + luni_send(paste_buffer + paste_pos, n); paste_pos += n; if (paste_pos < paste_len) {