X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/4f892125d8842ae40448e7ecdd994fb6d26313a7..f2003e3230fa6f3a1cc5d60256d1a19c8b0022d5:/terminal.c diff --git a/terminal.c b/terminal.c index 5245cdb7..1e2d50c6 100644 --- a/terminal.c +++ b/terminal.c @@ -63,7 +63,7 @@ static unsigned long curstype; /* type of cursor on real screen */ struct beeptime { struct beeptime *next; - long ticks; + unsigned long ticks; }; static struct beeptime *beephead, *beeptail; int nbeeps; @@ -86,6 +86,7 @@ typedef struct { #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) ) +static bufchain inbuf; /* terminal input buffer */ static pos curs; /* cursor */ static pos savecurs; /* saved cursor position */ static int marg_t, marg_b; /* scroll margins */ @@ -96,7 +97,7 @@ 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 unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */ static int cursor_on; /* cursor enabled flag */ static int reset_132; /* Flag ESC c resets to 80 cols */ static int use_bce; /* Use Background coloured erase */ @@ -291,7 +292,7 @@ static void power_on(void) big_cursor = 0; save_attr = curr_attr = ATTR_DEFAULT; term_editing = term_echoing = FALSE; - ldisc_send(NULL, 0); /* cause ldisc to notice changes */ + ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */ app_cursor_keys = cfg.app_cursor; app_keypad_keys = cfg.app_keypad; use_bce = cfg.bce; @@ -675,7 +676,7 @@ static void scroll(int topline, int botline, int lines, int sb) * selection), and also selanchor (for one being * selected as we speak). */ - seltop = sb ? -savelines : 0; + seltop = sb ? -savelines : topline; if (selstart.y >= seltop && selstart.y <= botline) { selstart.y--; @@ -847,7 +848,7 @@ static void insch(int n) */ static void toggle_mode(int mode, int query, int state) { - long ticks; + unsigned long ticks; if (query) switch (mode) { @@ -876,14 +877,20 @@ static void toggle_mode(int mode, int query, int state) * always be an actually _visible_ visual bell. */ ticks = GetTickCount(); - if (rvideo && !state && /* we're turning it off */ - ticks < rvbell_timeout) { /* and it's not long since it was turned on */ + /* turn off a previous vbell to avoid inconsistencies */ + if (ticks - vbell_startpoint >= VBELL_TIMEOUT) + in_vbell = FALSE; + if (rvideo && !state && /* we're turning it off... */ + (ticks - rvbell_startpoint) < VBELL_TIMEOUT) { /* ...soon */ + /* If there's no vbell timeout already, or this one lasts + * longer, replace vbell_timeout with ours. */ + if (!in_vbell || + (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT)) + vbell_startpoint = rvbell_startpoint; in_vbell = TRUE; /* we may clear rvideo but we set in_vbell */ - if (vbell_timeout < rvbell_timeout) /* don't move vbell end forward */ - vbell_timeout = rvbell_timeout; /* vbell end is at least then */ } else if (!rvideo && state) { /* This is an ON, so we notice the time and save it. */ - rvbell_timeout = ticks + VBELL_TIMEOUT; + rvbell_startpoint = ticks; } rvideo = state; seen_disp_event = TRUE; @@ -901,7 +908,7 @@ static void toggle_mode(int mode, int query, int state) break; case 10: /* set local edit mode */ term_editing = state; - ldisc_send(NULL, 0); /* cause ldisc to notice changes */ + ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */ break; case 25: /* enable/disable cursor */ compatibility2(OTHER, VT220); @@ -930,7 +937,7 @@ static void toggle_mode(int mode, int query, int state) break; case 12: /* set echo mode */ term_echoing = !state; - ldisc_send(NULL, 0); /* cause ldisc to notice changes */ + ldisc_send(NULL, 0, 0); /* cause ldisc to notice changes */ break; case 20: /* Return sends ... */ cr_lf_return = state; @@ -973,20 +980,38 @@ static void do_osc(void) */ void term_out(void) { - int c, inbuf_reap; + int c, unget; + unsigned char localbuf[256], *chars; + int nchars = 0; + + unget = -1; + + while (nchars > 0 || bufchain_size(&inbuf) > 0) { + if (unget == -1) { + if (nchars == 0) { + void *ret; + bufchain_prefix(&inbuf, &ret, &nchars); + if (nchars > sizeof(localbuf)) + nchars = sizeof(localbuf); + memcpy(localbuf, ret, nchars); + bufchain_consume(&inbuf, nchars); + chars = localbuf; + assert(chars != NULL); + } + c = *chars++; + nchars--; - /* - * Optionally log the session traffic to a file. Useful for - * debugging and possibly also useful for actual logging. - */ - if (cfg.logtype == LGTYP_DEBUG) - for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) { - logtraffic((unsigned char) inbuf[inbuf_reap], LGTYP_DEBUG); + /* + * Optionally log the session traffic to a file. Useful for + * debugging and possibly also useful for actual logging. + */ + if (cfg.logtype == LGTYP_DEBUG) + logtraffic((unsigned char) c, LGTYP_DEBUG); + } else { + c = unget; + unget = -1; } - for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) { - c = inbuf[inbuf_reap]; - /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even * be able to display 8-bit characters, but I'll let that go 'cause * of i18n. @@ -1029,7 +1054,7 @@ void term_out(void) case 4: case 5: if ((c & 0xC0) != 0x80) { - inbuf_reap--; + unget = c; c = UCSERR; utf_state = 0; break; @@ -1169,13 +1194,13 @@ void term_out(void) } else *d++ = *s; } - lpage_send(CP_ACP, abuf, d - abuf); + lpage_send(CP_ACP, abuf, d - abuf, 0); } break; case '\007': { struct beeptime *newbeep; - long ticks; + unsigned long ticks; ticks = GetTickCount(); @@ -1206,7 +1231,7 @@ void term_out(void) } if (cfg.bellovl && beep_overloaded && - ticks - lastbeep >= cfg.bellovl_s) { + ticks - lastbeep >= (unsigned)cfg.bellovl_s) { /* * If we're currently overloaded and the * last beep was more than s seconds ago, @@ -1231,7 +1256,7 @@ void term_out(void) beep(cfg.beep); if (cfg.beep == BELL_VISUAL) { in_vbell = TRUE; - vbell_timeout = ticks + VBELL_TIMEOUT; + vbell_startpoint = ticks; term_update(); } } @@ -1475,7 +1500,7 @@ void term_out(void) break; case 'Z': /* terminal type query */ compatibility(VT100); - ldisc_send(id_string, strlen(id_string)); + ldisc_send(id_string, strlen(id_string), 0); break; case 'c': /* restore power-on settings */ compatibility(VT100); @@ -1627,7 +1652,7 @@ void term_out(void) 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); + ldisc_send("\033[>0;136;0c", 11, 0); break; case 'a': /* move right N cols */ compatibility(ANSI); @@ -1723,16 +1748,16 @@ void term_out(void) case 'c': /* terminal type query */ compatibility(VT100); /* This is the response for a VT102 */ - ldisc_send(id_string, strlen(id_string)); + ldisc_send(id_string, strlen(id_string), 0); break; case 'n': /* cursor position query */ if (esc_args[0] == 6) { char buf[32]; sprintf(buf, "\033[%d;%dR", curs.y + 1, curs.x + 1); - ldisc_send(buf, strlen(buf)); + ldisc_send(buf, strlen(buf), 0); } else if (esc_args[0] == 5) { - ldisc_send("\033[0n", 4); + ldisc_send("\033[0n", 4, 0); } break; case 'h': /* toggle modes to high */ @@ -1992,7 +2017,7 @@ void term_out(void) if (i == 0 || i == 1) { strcpy(buf, "\033[2;1;1;112;112;1;0x"); buf[2] += i; - ldisc_send(buf, 20); + ldisc_send(buf, 20, 0); } } break; @@ -2309,7 +2334,7 @@ void term_out(void) termstate = VT52_Y1; break; case 'Z': - ldisc_send("\033/Z", 3); + ldisc_send("\033/Z", 3, 0); break; case '=': app_keypad_keys = TRUE; @@ -2480,7 +2505,6 @@ void term_out(void) check_selection(curs, cursplus); } } - inbuf_head = 0; } #if 0 @@ -2510,16 +2534,16 @@ static void do_paint(Context ctx, int may_optimise) pos scrpos; char ch[1024]; long cursor_background = ERASE_CHAR; - long ticks; + unsigned long ticks; /* * Check the visual bell state. */ if (in_vbell) { ticks = GetTickCount(); - if (ticks - vbell_timeout >= 0) - in_vbell = FALSE; - } + if (ticks - vbell_startpoint >= VBELL_TIMEOUT) + in_vbell = FALSE; + } rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0); @@ -3043,6 +3067,7 @@ static pos sel_spread_half(pos p, int dir) { unsigned long *ldata; short wvalue; + int topy = -count234(scrollback); ldata = lineptr(p.y); @@ -3069,11 +3094,47 @@ static pos sel_spread_half(pos p, int dir) */ wvalue = wordtype(ldata[p.x]); if (dir == +1) { - while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue) - p.x++; + while (1) { + if (p.x < cols-1) { + if (wordtype(ldata[p.x + 1]) == wvalue) + p.x++; + else + break; + } else { + if (ldata[cols] & LATTR_WRAPPED) { + unsigned long *ldata2; + ldata2 = lineptr(p.y+1); + if (wordtype(ldata2[0]) == wvalue) { + p.x = 0; + p.y++; + ldata = ldata2; + } else + break; + } else + break; + } + } } else { - while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue) - p.x--; + while (1) { + if (p.x > 0) { + if (wordtype(ldata[p.x - 1]) == wvalue) + p.x--; + else + break; + } else { + unsigned long *ldata2; + if (p.y <= topy) + break; + ldata2 = lineptr(p.y-1); + if ((ldata2[cols] & LATTR_WRAPPED) && + wordtype(ldata2[cols-1]) == wvalue) { + p.x = cols-1; + p.y--; + ldata = ldata2; + } else + break; + } + } } break; case SM_LINE: @@ -3132,7 +3193,7 @@ void term_do_paste(void) /* Assume a small paste will be OK in one go. */ if (paste_len < 256) { - luni_send(paste_buffer, paste_len); + luni_send(paste_buffer, paste_len, 0); if (paste_buffer) sfree(paste_buffer); paste_buffer = 0; @@ -3223,7 +3284,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y, c = x + 33; sprintf(abuf, "\033[M%c%c%c", encstate, c, r); - ldisc_send(abuf, 6); + ldisc_send(abuf, 6, 0); return; } @@ -3319,7 +3380,7 @@ void term_paste() if (paste_buffer[paste_pos + n++] == '\r') break; } - luni_send(paste_buffer + paste_pos, n); + luni_send(paste_buffer + paste_pos, n, 0); paste_pos += n; if (paste_pos < paste_len) { @@ -3358,18 +3419,15 @@ int term_ldisc(int option) */ int from_backend(int is_stderr, char *data, int len) { - while (len--) { - if (inbuf_head >= INBUF_SIZE) - term_out(); - inbuf[inbuf_head++] = *data++; - } + bufchain_add(&inbuf, data, len); /* - * 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. - * + * term_out() always completely empties inbuf. Therefore, + * there's no reason at all to return anything other than zero + * from this function, because there _can't_ be a question of + * the remote side needing to wait until term_out() has cleared + * a backlog. + * * 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 @@ -3379,7 +3437,7 @@ int from_backend(int is_stderr, char *data, int len) * 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;