diff --git a/putty.h b/putty.h index 0753d7cf..c4e8d6a1 100644 --- a/putty.h +++ b/putty.h @@ -1593,6 +1593,11 @@ struct TermWinVtable { * object, because that doesn't happen until term_init * returns. */ void (*palette_get_overrides)(TermWin *, Terminal *); + + /* Notify the front end that the terminal's buffer of unprocessed + * output has reduced. (Front ends will likely pass this straight + * on to backend_unthrottle.) */ + void (*unthrottle)(TermWin *, size_t bufsize); }; static inline bool win_setup_draw_ctx(TermWin *win) @@ -1649,6 +1654,8 @@ static inline void win_palette_set( { win->vt->palette_set(win, start, ncolours, colours); } static inline void win_palette_get_overrides(TermWin *win, Terminal *term) { win->vt->palette_get_overrides(win, term); } +static inline void win_unthrottle(TermWin *win, size_t size) +{ win->vt->unthrottle(win, size); } /* * Global functions not specific to a connection instance. diff --git a/terminal/terminal.c b/terminal/terminal.c index 3b0c68d4..6efd9cfe 100644 --- a/terminal/terminal.c +++ b/terminal/terminal.c @@ -98,7 +98,7 @@ static void deselect(Terminal *); static void term_print_finish(Terminal *); static void scroll(Terminal *, int, int, int, bool); static void parse_optionalrgb(optionalrgb *out, unsigned *values); -static void term_added_data(Terminal *term); +static void term_added_data(Terminal *term, bool); static void term_update_raw_mouse_mode(Terminal *term); static void term_out_cb(void *); @@ -3491,7 +3491,7 @@ static inline void term_keyinput_internal( int true_len = len >= 0 ? len : strlen(buf); bufchain_add(&term->inbuf, buf, true_len); - term_added_data(term); + term_added_data(term, false); } if (interactive) term_bracketed_paste_stop(term); @@ -3639,7 +3639,7 @@ unsigned long term_translate( * in-memory display. There's a big state machine in here to * process escape sequences... */ -static void term_out(Terminal *term) +static void term_out(Terminal *term, bool called_from_term_data) { unsigned long c; int unget; @@ -5589,6 +5589,9 @@ static void term_out(Terminal *term) bufchain_consume(&term->inbuf, nchars_used); + if (!called_from_term_data) + win_unthrottle(term->win, bufchain_size(&term->inbuf)); + term_print_flush(term); if (term->logflush && term->logctx) logflush(term->logctx); @@ -5597,7 +5600,7 @@ static void term_out(Terminal *term) /* Wrapper on term_out with the right prototype to be a toplevel callback */ void term_out_cb(void *ctx) { - term_out((Terminal *)ctx); + term_out((Terminal *)ctx, false); } /* @@ -7292,7 +7295,7 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, * should make sure to write any pending output if one has just * finished. */ - term_out(term); + term_out(term, false); term_schedule_update(term); } @@ -7605,15 +7608,15 @@ void term_lost_clipboard_ownership(Terminal *term, int clipboard) * should make sure to write any pending output if one has just * finished. */ - term_out(term); + term_out(term, false); } -static void term_added_data(Terminal *term) +static void term_added_data(Terminal *term, bool called_from_term_data) { if (!term->in_term_out) { term->in_term_out = true; term_reset_cblink(term); - term_out(term); + term_out(term, called_from_term_data); term->in_term_out = false; } } @@ -7621,28 +7624,8 @@ static void term_added_data(Terminal *term) size_t term_data(Terminal *term, const void *data, size_t len) { bufchain_add(&term->inbuf, data, len); - term_added_data(term); - - /* - * 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 SSH-2 - 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 SSH-1 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; + term_added_data(term, true); + return bufchain_size(&term->inbuf); } void term_provide_logctx(Terminal *term, LogContext *logctx) diff --git a/test/fuzzterm.c b/test/fuzzterm.c index f1477054..02de7bf6 100644 --- a/test/fuzzterm.c +++ b/test/fuzzterm.c @@ -91,6 +91,7 @@ static void fuzz_set_zorder(TermWin *tw, bool top) {} static void fuzz_palette_set(TermWin *tw, unsigned start, unsigned ncolours, const rgb *colours) {} static void fuzz_palette_get_overrides(TermWin *tw, Terminal *term) {} +static void fuzz_unthrottle(TermWin *tw, size_t size) {} static const TermWinVtable fuzz_termwin_vt = { .setup_draw_ctx = fuzz_setup_draw_ctx, @@ -115,6 +116,7 @@ static const TermWinVtable fuzz_termwin_vt = { .set_zorder = fuzz_set_zorder, .palette_set = fuzz_palette_set, .palette_get_overrides = fuzz_palette_get_overrides, + .unthrottle = fuzz_unthrottle, }; void ldisc_send(Ldisc *ldisc, const void *buf, int len, bool interactive) {} diff --git a/unix/window.c b/unix/window.c index 2fadd393..4c57b020 100644 --- a/unix/window.c +++ b/unix/window.c @@ -326,6 +326,13 @@ static size_t gtk_seat_output(Seat *seat, SeatOutputType type, return term_data(inst->term, data, len); } +static void gtkwin_unthrottle(TermWin *win, size_t bufsize) +{ + GtkFrontend *inst = container_of(win, GtkFrontend, termwin); + if (inst->backend) + backend_unthrottle(inst->backend, bufsize); +} + static bool gtk_seat_eof(Seat *seat) { /* GtkFrontend *inst = container_of(seat, GtkFrontend, seat); */ @@ -5156,6 +5163,7 @@ static const TermWinVtable gtk_termwin_vt = { .set_zorder = gtkwin_set_zorder, .palette_set = gtkwin_palette_set, .palette_get_overrides = gtkwin_palette_get_overrides, + .unthrottle = gtkwin_unthrottle, }; void new_session_window(Conf *conf, const char *geometry_string) diff --git a/windows/window.c b/windows/window.c index aead39f4..b1a884dd 100644 --- a/windows/window.c +++ b/windows/window.c @@ -259,6 +259,7 @@ static void wintw_move(TermWin *, int x, int y); static void wintw_set_zorder(TermWin *, bool top); static void wintw_palette_set(TermWin *, unsigned, unsigned, const rgb *); static void wintw_palette_get_overrides(TermWin *, Terminal *); +static void wintw_unthrottle(TermWin *win, size_t bufsize); static const TermWinVtable windows_termwin_vt = { .setup_draw_ctx = wintw_setup_draw_ctx, @@ -284,6 +285,7 @@ static const TermWinVtable windows_termwin_vt = { .set_zorder = wintw_set_zorder, .palette_set = wintw_palette_set, .palette_get_overrides = wintw_palette_get_overrides, + .unthrottle = wintw_unthrottle, }; static TermWin wintw[1]; @@ -5774,6 +5776,12 @@ static size_t win_seat_output(Seat *seat, SeatOutputType type, return term_data(term, data, len); } +static void wintw_unthrottle(TermWin *win, size_t bufsize) +{ + if (backend) + backend_unthrottle(backend, bufsize); +} + static bool win_seat_eof(Seat *seat) { return true; /* do respond to incoming EOF with outgoing */