X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/4eeb7d098fc3c6fb29a4ba0da78edbf83f88dce1..3ad9d396e3e57477b4da4b20665ca33edd5d7f67:/terminal.c diff --git a/terminal.c b/terminal.c index 9abb8483..8f8ec157 100644 --- a/terminal.c +++ b/terminal.c @@ -82,7 +82,7 @@ typedef struct { #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) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).x ) +#define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x ) #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) ) #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) ) @@ -94,6 +94,7 @@ static int wrap, wrapnext; /* wrap flags */ static int insert; /* insert-mode flag */ static int cset; /* 0 or 1: which char set */ static int save_cset, save_csattr; /* saved with cursor position */ +static int save_utf; /* saved with cursor position */ static int rvideo; /* global reverse video flag */ static int rvbell_timeout; /* for ESC[?5hESC[?5l vbell */ static int cursor_on; /* cursor enabled flag */ @@ -104,6 +105,7 @@ 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 sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */ 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. */ @@ -116,7 +118,7 @@ static unsigned long cset_attr[2]; /* * Saved settings on the alternate screen. */ -static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset; +static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf; static int alt_t, alt_b; static int alt_which; @@ -201,15 +203,40 @@ static FILE *lgfp = NULL; static void logtraffic(unsigned char c, int logmode); /* + * Resize a line to make it `cols' columns wide. + */ +unsigned long *resizeline(unsigned long *line, int cols) +{ + int i, oldlen; + unsigned long lineattrs; + + if (line[0] != (unsigned long)cols) { + /* + * This line is the wrong length, which probably means it + * hasn't been accessed since a resize. Resize it now. + */ + oldlen = line[0]; + lineattrs = line[oldlen + 1]; + line = srealloc(line, TSIZE * (2 + cols)); + line[0] = cols; + for (i = oldlen; i < cols; i++) + line[i + 1] = ERASE_CHAR; + line[cols + 1] = lineattrs & LATTR_MODE; + } + + return line; +} + +/* * Retrieve a line of the screen or of the scrollback, according to * whether the y coordinate is non-negative or negative * (respectively). */ unsigned long *lineptr(int y, int lineno) { - unsigned long *line, lineattrs; + unsigned long *line, *newline; tree234 *whichtree; - int i, treeindex, oldlen; + int treeindex; if (y >= 0) { whichtree = screen; @@ -223,20 +250,11 @@ unsigned long *lineptr(int y, int lineno) /* We assume that we don't screw up and retrieve something out of range. */ assert(line != NULL); - if (line[0] != cols) { - /* - * This line is the wrong length, which probably means it - * hasn't been accessed since a resize. Resize it now. - */ - oldlen = line[0]; - lineattrs = line[oldlen + 1]; + newline = resizeline(line, cols); + if (newline != line) { delpos234(whichtree, treeindex); - line = srealloc(line, TSIZE * (2 + cols)); - line[0] = cols; - for (i = oldlen; i < cols; i++) - line[i + 1] = ERASE_CHAR; - line[cols + 1] = lineattrs & LATTR_MODE; - addpos234(whichtree, line, treeindex); + addpos234(whichtree, newline, treeindex); + line = newline; } return line + 1; @@ -263,6 +281,8 @@ static void power_on(void) alt_wnext = wrapnext = alt_ins = insert = FALSE; alt_wrap = wrap = cfg.wrap_mode; alt_cset = cset = 0; + alt_utf = utf = 0; + alt_sco_acs = sco_acs = 0; cset_attr[0] = cset_attr[1] = ATTR_ASCII; rvideo = 0; in_vbell = FALSE; @@ -298,11 +318,12 @@ void term_update(void) Context ctx; ctx = get_ctx(); if (ctx) { + if (seen_disp_event) + update_sbar(); if ((seen_key_event && (cfg.scroll_on_key)) || (seen_disp_event && (cfg.scroll_on_disp))) { disptop = 0; /* return to main screen */ seen_disp_event = seen_key_event = 0; - update_sbar(); } do_paint(ctx, TRUE); sys_cursor(curs.x, curs.y - disptop); @@ -358,9 +379,9 @@ void term_init(void) */ void term_size(int newrows, int newcols, int newsavelines) { - tree234 *newsb, *newscreen, *newalt; - unsigned long *newdisp, *oldline, *line; - int i, j, ccols; + tree234 *newalt; + unsigned long *newdisp, *line; + int i, j; int sblen; int save_alt_which = alt_which; @@ -520,6 +541,12 @@ static void swap_screen(int which) t = cset; cset = alt_cset; alt_cset = t; + t = utf; + utf = alt_utf; + alt_utf = t; + t = sco_acs; + sco_acs = alt_sco_acs; + alt_sco_acs = t; fix_cpos; } @@ -567,6 +594,7 @@ static void scroll(int topline, int botline, int lines, int sb) if (lines < 0) { while (lines < 0) { line = delpos234(screen, botline); + line = resizeline(line, cols); for (i = 0; i < cols; i++) line[i + 1] = erase_char; line[cols + 1] = 0; @@ -609,6 +637,7 @@ static void scroll(int topline, int botline, int lines, int sb) addpos234(scrollback, line, sblen); line = line2; } + line = resizeline(line, cols); for (i = 0; i < cols; i++) line[i + 1] = erase_char; line[cols + 1] = 0; @@ -671,7 +700,9 @@ static void save_cursor(int save) savecurs = curs; save_attr = curr_attr; save_cset = cset; + save_utf = utf; save_csattr = cset_attr[cset]; + save_sco_acs = sco_acs; } else { curs = savecurs; /* Make sure the window hasn't shrunk since the save */ @@ -682,10 +713,13 @@ static void save_cursor(int save) curr_attr = save_attr; cset = save_cset; + utf = save_utf; cset_attr[cset] = save_csattr; + sco_acs = save_sco_acs; fix_cpos; if (use_bce) - erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); } } @@ -717,6 +751,7 @@ static void erase_lots(int line_only, int from_begin, int to_end) } if (!to_end) { end = curs; + incpos(end); } check_selection(start, end); @@ -726,7 +761,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) + if (start.x == cols && !erase_lattr) ldata[start.x] &= ~LATTR_WRAPPED; else ldata[start.x] = erase_char; @@ -915,18 +950,15 @@ void term_out(void) /* First see about all those translations. */ if (termstate == TOPLEVEL) { - if (utf) + if (in_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; + /* UTF-8 must be stateless so we ignore iso2022. */ + if (unitab_ctrl[c] != 0xFF) + c = unitab_ctrl[c]; + else c = ((unsigned char)c) | ATTR_ASCII; + break; } else if ((c & 0xe0) == 0xc0) { utf_size = utf_state = 1; utf_char = (c & 0x1f); @@ -1005,8 +1037,14 @@ void term_out(void) if (c >= 0x10000) c = 0xFFFD; break; + } + /* Are we in the nasty ACS mode? Note: no sco in utf mode. */ + else if(sco_acs && + (c!='\033' && c!='\n' && c!='\r' && c!='\b')) + { + if (sco_acs == 2) c ^= 0x80; + c |= ATTR_SCOACS; } else { - evil_jump:; switch (cset_attr[cset]) { /* * Linedraw characters are different from 'ESC ( B' @@ -1033,6 +1071,9 @@ void term_out(void) else c = ((unsigned char) c) | ATTR_ASCII; break; + case ATTR_SCOACS: + if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS; + break; } } } @@ -1142,11 +1183,9 @@ void term_out(void) /* * Perform an actual beep if we're not overloaded. */ - if ((!cfg.bellovl || !beep_overloaded) - && cfg.beep != 0) { - if (cfg.beep != 2) - beep(cfg.beep); - else if (cfg.beep == 2) { + if (!cfg.bellovl || !beep_overloaded) { + beep(cfg.beep); + if (cfg.beep == BELL_VISUAL) { in_vbell = TRUE; vbell_timeout = ticks + VBELL_TIMEOUT; term_update(); @@ -1444,7 +1483,7 @@ void term_out(void) case ANSI('5', '#'): nlattr = LATTR_NORM; break; - case ANSI('6', '#'): + default: /* spiritually case ANSI('6', '#'): */ nlattr = LATTR_WIDE; break; } @@ -1466,6 +1505,10 @@ void term_out(void) compatibility(VT100); cset_attr[0] = ATTR_LINEDRW; break; + case ANSI('U', '('): + compatibility(OTHER); + cset_attr[0] = ATTR_SCOACS; + break; case ANSI('A', ')'): compatibility(VT100); @@ -1479,6 +1522,10 @@ void term_out(void) compatibility(VT100); cset_attr[1] = ATTR_LINEDRW; break; + case ANSI('U', ')'): + compatibility(OTHER); + cset_attr[1] = ATTR_SCOACS; + break; case ANSI('8', '%'): /* Old Linux code */ case ANSI('G', '%'): @@ -1487,8 +1534,7 @@ void term_out(void) break; case ANSI('@', '%'): compatibility(OTHER); - if (line_codepage != CP_UTF8) - utf = 0; + utf = 0; break; } break; @@ -1522,18 +1568,20 @@ void term_out(void) break; case 'e': /* move down N lines */ compatibility(ANSI); + /* FALLTHROUGH */ case 'B': move(curs.x, curs.y + def(esc_args[0], 1), 1); seen_disp_event = TRUE; break; - case 'a': /* move right N cols */ - compatibility(ANSI); case ANSI('c', '>'): /* report xterm version */ compatibility(OTHER); /* this reports xterm version 136 so that VIM can use the drag messages from the mouse reporting */ ldisc_send("\033[>0;136;0c", 11); break; + case 'a': /* move right N cols */ + compatibility(ANSI); + /* FALLTHROUGH */ case 'C': move(curs.x + def(esc_args[0], 1), curs.y, 1); seen_disp_event = TRUE; @@ -1744,6 +1792,15 @@ void term_out(void) case 7: /* enable reverse video */ curr_attr |= ATTR_REVERSE; break; + case 10: /* SCO acs off */ + compatibility(SCOANSI); + sco_acs = 0; break; + case 11: /* SCO acs on */ + compatibility(SCOANSI); + sco_acs = 1; break; + case 12: /* SCO acs on flipped */ + compatibility(SCOANSI); + sco_acs = 2; break; case 22: /* disable bold */ compatibility2(OTHER, VT220); curr_attr &= ~ATTR_BOLD; @@ -1797,11 +1854,9 @@ void term_out(void) } } if (use_bce) - erase_char = - (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK))); } break; case 's': /* save cursor */ @@ -1894,15 +1949,29 @@ void term_out(void) } } break; + case 'Z': /* BackTab for xterm */ + compatibility(OTHER); + { + int i = def(esc_args[0], 1); + pos old_curs = curs; + + for(;i>0 && curs.x>0; i--) { + do { + curs.x--; + } while (curs.x >0 && !tabs[curs.x]); + } + fix_cpos; + check_selection(old_curs, curs); + } + break; case ANSI('L', '='): compatibility(OTHER); use_bce = (esc_args[0] <= 0); erase_char = ERASE_CHAR; if (use_bce) - erase_char = - (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK))); break; case ANSI('E', '='): compatibility(OTHER); @@ -2064,8 +2133,10 @@ void term_out(void) val = c - 'A' + 10; else if (c >= 'a' && c <= 'a' + max - 10) val = c - 'a' + 10; - else + else { termstate = TOPLEVEL; + break; + } osc_string[osc_strlen++] = val; if (osc_strlen >= 7) { palette_set(osc_string[0], @@ -2292,10 +2363,9 @@ void term_out(void) vt52_bold = FALSE; curr_attr = ATTR_DEFAULT; if (use_bce) - erase_char = (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & + (ATTR_FGMASK | ATTR_BGMASK))); break; case 'S': /* compatibility(VI50) */ @@ -2337,10 +2407,8 @@ void term_out(void) curr_attr |= ATTR_BOLD; if (use_bce) - erase_char = (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); break; case VT52_BG: termstate = TOPLEVEL; @@ -2353,12 +2421,11 @@ void term_out(void) curr_attr |= ATTR_BLINK; if (use_bce) - erase_char = (' ' | - (curr_attr & - (ATTR_FGMASK | ATTR_BGMASK | - ATTR_BLINK))); + erase_char = (' ' | ATTR_ASCII | + (curr_attr & (ATTR_FGMASK | ATTR_BGMASK))); break; #endif + default: break; /* placate gcc warning about enum use */ } if (selstate != NO_SELECTION) { pos cursplus = curs; @@ -2477,6 +2544,9 @@ static void do_paint(Context ctx, int may_optimise) case ATTR_LINEDRW: tchar = unitab_xterm[tchar & 0xFF]; break; + case ATTR_SCOACS: + tchar = unitab_scoacs[tchar&0xFF]; + break; } tattr |= (tchar & CSET_MASK); tchar &= CHAR_MASK; @@ -2711,6 +2781,9 @@ static void clipme(pos top, pos bottom) case ATTR_ASCII: uc = unitab_line[uc & 0xFF]; break; + case ATTR_SCOACS: + uc = unitab_scoacs[uc&0xFF]; + break; } switch (uc & CSET_MASK) { case ATTR_ACP: @@ -2871,6 +2944,9 @@ static int wordtype(int uc) case ATTR_ASCII: uc = unitab_line[uc & 0xFF]; break; + case ATTR_SCOACS: + uc = unitab_scoacs[uc&0xFF]; + break; } switch (uc & CSET_MASK) { case ATTR_ACP: @@ -2950,6 +3026,54 @@ static void sel_spread(void) incpos(selend); } +void term_do_paste(void) +{ + wchar_t *data; + int len; + + get_clip(&data, &len); + if (data) { + wchar_t *p, *q; + + if (paste_buffer) + sfree(paste_buffer); + paste_pos = paste_hold = paste_len = 0; + paste_buffer = smalloc(len * sizeof(wchar_t)); + + p = q = data; + while (p < data + len) { + while (p < data + len && + !(p <= data + len - sel_nl_sz && + !memcmp(p, sel_nl, sizeof(sel_nl)))) + p++; + + { + int i; + for (i = 0; i < p - q; i++) { + paste_buffer[paste_len++] = q[i]; + } + } + + if (p <= data + len - sel_nl_sz && + !memcmp(p, sel_nl, sizeof(sel_nl))) { + paste_buffer[paste_len++] = '\r'; + p += sel_nl_sz; + } + q = p; + } + + /* Assume a small paste will be OK in one go. */ + if (paste_len < 256) { + luni_send(paste_buffer, paste_len); + if (paste_buffer) + sfree(paste_buffer); + paste_buffer = 0; + paste_pos = paste_hold = paste_len = 0; + } + } + get_clip(NULL, NULL); +} + void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, int shift, int ctrl) { @@ -2976,7 +3100,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, if ((ldata[cols] & LATTR_MODE) != LATTR_NORM) selpoint.x /= 2; - if (xterm_mouse) { + if (xterm_mouse && !(cfg.mouse_override && shift)) { int encstate = 0, r, c; char abuf[16]; static int is_down = 0; @@ -2997,6 +3121,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, case MBT_WHEEL_DOWN: encstate = 0x61; break; + default: break; /* placate gcc warning about enum use */ } switch (a) { case MA_DRAG: @@ -3013,6 +3138,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, return; is_down = b; break; + default: break; /* placate gcc warning about enum use */ } if (shift) encstate += 0x04; @@ -3080,50 +3206,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, selstate = NO_SELECTION; } else if (b == MBT_PASTE && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) { - wchar_t *data; - int len; - - get_clip(&data, &len); - if (data) { - wchar_t *p, *q; - - if (paste_buffer) - sfree(paste_buffer); - paste_pos = paste_hold = paste_len = 0; - paste_buffer = smalloc(len * sizeof(wchar_t)); - - p = q = data; - while (p < data + len) { - while (p < data + len && - !(p <= data + len - sel_nl_sz && - !memcmp(p, sel_nl, sizeof(sel_nl)))) - p++; - - { - int i; - for (i = 0; i < p - q; i++) { - paste_buffer[paste_len++] = q[i]; - } - } - - if (p <= data + len - sel_nl_sz && - !memcmp(p, sel_nl, sizeof(sel_nl))) { - paste_buffer[paste_len++] = '\r'; - p += sel_nl_sz; - } - q = p; - } - - /* Assume a small paste will be OK in one go. */ - if (paste_len < 256) { - luni_send(paste_buffer, paste_len); - if (paste_buffer) - sfree(paste_buffer); - paste_buffer = 0; - paste_pos = paste_hold = paste_len = 0; - } - } - get_clip(NULL, NULL); + term_do_paste(); } term_update(); @@ -3198,13 +3281,33 @@ int term_ldisc(int option) /* * from_backend(), to get data from the backend for the terminal. */ -void from_backend(int is_stderr, char *data, int len) +int from_backend(int is_stderr, char *data, int len) { while (len--) { if (inbuf_head >= INBUF_SIZE) term_out(); inbuf[inbuf_head++] = *data++; } + + /* + * We process all stdout/stderr data immediately we receive it, + * and don't return until it's all gone. Therefore, there's no + * reason at all to return anything other than zero from this + * function. + * + * This is a slightly suboptimal way to deal with SSH2 - in + * principle, the window mechanism would allow us to continue + * to accept data on forwarded ports and X connections even + * while the terminal processing was going slowly - but we + * can't do the 100% right thing without moving the terminal + * processing into a separate thread, and that might hurt + * portability. So we manage stdout buffering the old SSH1 way: + * if the terminal processing goes slowly, the whole SSH + * connection stops accepting data until it's ready. + * + * In practice, I can't imagine this causing serious trouble. + */ + return 0; } /*