X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=terminal.c;h=ca47c8335f79eabaf581bd71b8b55f28b8718ee6;hb=9f9d72ec58642e91b4f93ee4405a8086ee2fb2f0;hp=ee88974b56326bca9cfcd0d602ef2761011562c6;hpb=7d394fc9e9c705770f6bde26b67df1b00ea82c64;p=PuTTY.git diff --git a/terminal.c b/terminal.c index ee88974b..ca47c833 100644 --- a/terminal.c +++ b/terminal.c @@ -65,7 +65,7 @@ #define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 ) -char *EMPTY_WINDOW_TITLE = ""; +const char *EMPTY_WINDOW_TITLE = ""; const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; @@ -98,6 +98,7 @@ const wchar_t sel_nl[] = SEL_NL; static void resizeline(Terminal *, termline *, int); static termline *lineptr(Terminal *, int, int, int); static void unlineptr(termline *); +static void check_line_size(Terminal *, termline *); static void do_paint(Terminal *, Context, int); static void erase_lots(Terminal *, int, int, int); static int find_last_nonempty_line(Terminal *, tree234 *); @@ -1053,8 +1054,25 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen) } assert(line != NULL); - resizeline(term, line, term->cols); - /* FIXME: should we sort the compressed scrollback out here? */ + /* + * Here we resize lines to _at least_ the right length, but we + * don't truncate them. Truncation is done as a side effect of + * modifying the line. + * + * The point of this policy is to try to arrange that resizing the + * terminal window repeatedly - e.g. successive steps in an X11 + * opaque window-resize drag, or resizing as a side effect of + * retiling by tiling WMs such as xmonad - does not throw away + * data gratuitously. Specifically, we want a sequence of resize + * operations with no terminal output between them to have the + * same effect as a single resize to the ultimate terminal size, + * and also (for the case in which xmonad narrows a window that's + * scrolling things) we want scrolling up new text at the bottom + * of a narrowed window to avoid truncating lines further up when + * the window is re-widened. + */ + if (term->cols > line->cols) + resizeline(term, line, term->cols); return line; } @@ -1062,6 +1080,22 @@ static termline *lineptr(Terminal *term, int y, int lineno, int screen) #define lineptr(x) (lineptr)(term,x,__LINE__,FALSE) #define scrlineptr(x) (lineptr)(term,x,__LINE__,TRUE) +/* + * Coerce a termline to the terminal's current width. Unlike the + * optional resize in lineptr() above, this is potentially destructive + * of text, since it can shrink as well as grow the line. + * + * We call this whenever a termline is actually going to be modified. + * Helpfully, putting a single call to this function in check_boundary + * deals with _nearly_ all such cases, leaving only a few things like + * bulk erase and ESC#8 to handle separately. + */ +static void check_line_size(Terminal *term, termline *line) +{ + if (term->cols != line->cols) /* trivial optimisation */ + resizeline(term, line, term->cols); +} + static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); @@ -1316,7 +1350,7 @@ void term_pwron(Terminal *term, int clear) { power_on(term, clear); if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); term->disptop = 0; deselect(term); term_update(term); @@ -1493,12 +1527,44 @@ void term_reconfig(Terminal *term, Conf *conf) void term_clrsb(Terminal *term) { unsigned char *line; + int i; + + /* + * Scroll forward to the current screen, if we were back in the + * scrollback somewhere until now. + */ term->disptop = 0; + + /* + * Clear the actual scrollback. + */ while ((line = delpos234(term->scrollback, 0)) != NULL) { sfree(line); /* this is compressed data, not a termline */ } + + /* + * When clearing the scrollback, we also truncate any termlines on + * the current screen which have remembered data from a previous + * larger window size. Rationale: clearing the scrollback is + * sometimes done to protect privacy, so the user intention is + * specifically that we should not retain evidence of what + * previously happened in the terminal, and that ought to include + * evidence to the right as well as evidence above. + */ + for (i = 0; i < term->rows; i++) + check_line_size(term, scrlineptr(i)); + + /* + * There are now no lines of real scrollback which can be pulled + * back into the screen by a resize, and no lines of the alternate + * screen which should be displayed as if part of the scrollback. + */ term->tempsblines = 0; term->alt_sblines = 0; + + /* + * Update the scrollbar to reflect the new state of the world. + */ update_sbar(term); } @@ -2281,6 +2347,7 @@ static void check_boundary(Terminal *term, int x, int y) return; ldata = scrlineptr(y); + check_line_size(term, ldata); if (x == term->cols) { ldata->lattr &= ~LATTR_WRAPPED2; } else { @@ -2351,6 +2418,7 @@ static void erase_lots(Terminal *term, } else { termline *ldata = scrlineptr(start.y); while (poslt(start, end)) { + check_line_size(term, ldata); if (start.x == term->cols) { if (!erase_lattr) ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2); @@ -2506,7 +2574,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) case 10: /* DECEDM: set local edit mode */ term->term_editing = state; if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); break; case 25: /* DECTCEM: enable/disable cursor */ compatibility2(OTHER, VT220); @@ -2570,7 +2638,7 @@ static void toggle_mode(Terminal *term, int mode, int query, int state) case 12: /* SRM: set echo mode */ term->term_echoing = !state; if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); break; case 20: /* LNM: Return sends ... */ term->cr_lf_return = state; @@ -3293,7 +3361,7 @@ static void term_out(Terminal *term) compatibility(VT100); power_on(term, TRUE); if (term->ldisc) /* cause ldisc to notice changes */ - ldisc_send(term->ldisc, NULL, 0, 0); + ldisc_echoedit_update(term->ldisc); if (term->reset_132) { if (!term->no_remote_resize) request_resize(term->frontend, 80, term->rows); @@ -3317,6 +3385,7 @@ static void term_out(Terminal *term) for (i = 0; i < term->rows; i++) { ldata = scrlineptr(i); + check_line_size(term, ldata); for (j = 0; j < term->cols; j++) { copy_termchar(ldata, j, &term->basic_erase_char); @@ -3341,6 +3410,7 @@ static void term_out(Terminal *term) compatibility(VT100); { int nlattr; + termline *ldata; switch (ANSI(c, term->esc_query)) { case ANSI('3', '#'): /* DECDHL: 2*height, top */ @@ -3356,7 +3426,9 @@ static void term_out(Terminal *term) nlattr = LATTR_WIDE; break; } - scrlineptr(term->curs.y)->lattr = nlattr; + ldata = scrlineptr(term->curs.y); + check_line_size(term, ldata); + ldata->lattr = nlattr; } break; /* GZD4: G0 designate 94-set */ @@ -3868,7 +3940,8 @@ static void term_out(Terminal *term) switch (term->esc_args[0]) { int x, y, len; - char buf[80], *p; + char buf[80]; + const char *p; case 1: set_iconic(term->frontend, FALSE); break; @@ -3923,7 +3996,9 @@ static void term_out(Terminal *term) case 13: if (term->ldisc) { get_window_pos(term->frontend, &x, &y); - len = sprintf(buf, "\033[3;%d;%dt", x, y); + len = sprintf(buf, "\033[3;%u;%ut", + (unsigned)x, + (unsigned)y); ldisc_send(term->ldisc, buf, len, 0); } break; @@ -5968,7 +6043,8 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, } else if (c <= 223 && r <= 223) { len = sprintf(abuf, "\033[M%c%c%c", encstate + 32, c + 32, r + 32); } - ldisc_send(term->ldisc, abuf, len, 0); + if (len > 0) + ldisc_send(term->ldisc, abuf, len, 0); } return; } @@ -6002,6 +6078,19 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, sel_spread(term); } else if ((bcooked == MBT_SELECT && a == MA_DRAG) || (bcooked == MBT_EXTEND && a != MA_RELEASE)) { + if (a == MA_DRAG && + (term->selstate == NO_SELECTION || term->selstate == SELECTED)) { + /* + * This can happen if a front end has passed us a MA_DRAG + * without a prior MA_CLICK. OS X GTK does so, for + * example, if the initial button press was eaten by the + * WM when it activated the window in the first place. The + * nicest thing to do in this situation is to ignore + * further drags, and wait for the user to click in the + * window again properly if they want to select. + */ + return; + } if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint)) return; if (bcooked == MBT_EXTEND && a != MA_DRAG && @@ -6245,7 +6334,7 @@ void term_set_focus(Terminal *term, int has_focus) */ char *term_get_ttymode(Terminal *term, const char *mode) { - char *val = NULL; + const char *val = NULL; if (strcmp(mode, "ERASE") == 0) { val = term->bksp_is_delete ? "^?" : "^H"; } @@ -6265,7 +6354,7 @@ struct term_userpass_state { * input. */ int term_get_userpass_input(Terminal *term, prompts_t *p, - unsigned char *in, int inlen) + const unsigned char *in, int inlen) { struct term_userpass_state *s = (struct term_userpass_state *)p->data; if (!s) {