From: Simon Tatham Date: Sat, 17 Aug 2013 16:06:12 +0000 (+0000) Subject: Revamp the terminal paste mechanism using toplevel callbacks. X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=commitdiff_plain;h=310835f6df5fa118dd8e9a248bcc57b92e889303;p=PuTTY_svn.git Revamp the terminal paste mechanism using toplevel callbacks. I've removed the ad-hoc front-end bodgery in the Windows and GTK ports to arrange for term_paste to be called at the right moments, and instead, terminal.c itself deals with knowing when to send the next chunk of pasted data using a combination of timers and the new top-level callback mechanism. As a happy side effect, it's now all in one place so I can actually understand what it's doing! It turns out that what all that confusing code was up to is: send a line of pasted data, and delay sending the next line until either a CR or LF is returned from the server (typically indicating that the pasted text has been received and echoed) or 450ms elapse, whichever comes first. git-svn-id: http://svn.tartarus.org/sgt/putty@10020 cda61777-01e9-0310-a592-d414129be87e --- diff --git a/putty.h b/putty.h index d1a5daeb..7fbb75ec 100644 --- a/putty.h +++ b/putty.h @@ -977,8 +977,6 @@ void term_update(Terminal *); void term_invalidate(Terminal *); void term_blink(Terminal *, int set_cursor); void term_do_paste(Terminal *); -int term_paste_pending(Terminal *); -void term_paste(Terminal *); void term_nopaste(Terminal *); int term_ldisc(Terminal *, int option); void term_copyall(Terminal *); diff --git a/terminal.c b/terminal.c index 113edc4f..e7095e0c 100644 --- a/terminal.c +++ b/terminal.c @@ -109,6 +109,9 @@ static void scroll(Terminal *, int, int, int, int); #ifdef OPTIMISE_SCROLL static void scroll_display(Terminal *, int, int, int); #endif /* OPTIMISE_SCROLL */ +static void term_resume_pasting(Terminal *term); +static void term_paste_callback(void *vterm); +static void term_paste_queue(Terminal *term, int timed); static termline *newline(Terminal *term, int cols, int bce) { @@ -1524,7 +1527,6 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, term->cblink_pending = term->tblink_pending = FALSE; term->paste_buffer = NULL; term->paste_len = 0; - term->last_paste = 0; bufchain_init(&term->inbuf); bufchain_init(&term->printer_buf); term->printing = term->only_printing = FALSE; @@ -2967,7 +2969,7 @@ static void term_out(Terminal *term) term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); - term->paste_hold = 0; + term_resume_pasting(term); if (term->crhaslf) { if (term->curs.y == term->marg_b) @@ -2998,7 +3000,7 @@ static void term_out(Terminal *term) term->curs.x = 0; term->wrapnext = FALSE; seen_disp_event(term); - term->paste_hold = 0; + term_resume_pasting(term); if (term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); break; @@ -5706,6 +5708,75 @@ static void sel_spread(Terminal *term) } } +static void term_resume_pasting(Terminal *term) +{ + expire_timer_context(&term->paste_timer_ctx); + term_paste_queue(term, FALSE); +} + +static void term_paste_timing_callback(void *vterm, unsigned long now) +{ + Terminal *term = *(Terminal **)vterm; + term_resume_pasting(term); +} + +static void term_paste_queue(Terminal *term, int timed) +{ + if (timed) { + /* + * Delay sending the rest of the paste buffer until we have + * seen a newline coming back from the server (indicating that + * it's absorbed the data we've sent so far). As a fallback, + * continue sending anyway after a longish timeout. + * + * We use the pointless structure field term->paste_timer_ctx + * (which is a Terminal *, and we'll make sure it points + * straight back to term) as our timer context, so that it can + * be distinguished from term itself. That way, if we see a + * reason to continue pasting before the timer goes off, we + * can cancel just this timer and none of the other terminal + * timers handling display updates, blinking text and cursor, + * and visual bells. + */ + term->paste_timer_ctx = term; + schedule_timer(450, term_paste_timing_callback, + &term->paste_timer_ctx); + } else { + /* + * Just arrange to call term_paste_callback from the top level + * at the next opportunity. + */ + queue_toplevel_callback(term_paste_callback, term); + } +} + +static void term_paste_callback(void *vterm) +{ + Terminal *term = (Terminal *)vterm; + + if (term->paste_len == 0) + return; + + while (term->paste_pos < term->paste_len) { + int n = 0; + while (n + term->paste_pos < term->paste_len) { + if (term->paste_buffer[term->paste_pos + n++] == '\015') + break; + } + if (term->ldisc) + luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0); + term->paste_pos += n; + + if (term->paste_pos < term->paste_len) { + term_paste_queue(term, TRUE); + return; + } + } + sfree(term->paste_buffer); + term->paste_buffer = NULL; + term->paste_len = 0; +} + void term_do_paste(Terminal *term) { wchar_t *data; @@ -5719,7 +5790,7 @@ void term_do_paste(Terminal *term) if (term->paste_buffer) sfree(term->paste_buffer); - term->paste_pos = term->paste_hold = term->paste_len = 0; + term->paste_pos = term->paste_len = 0; term->paste_buffer = snewn(len + 12, wchar_t); if (term->bracketed_paste) { @@ -5762,10 +5833,12 @@ void term_do_paste(Terminal *term) if (term->paste_buffer) sfree(term->paste_buffer); term->paste_buffer = 0; - term->paste_pos = term->paste_hold = term->paste_len = 0; + term->paste_pos = term->paste_len = 0; } } get_clip(term->frontend, NULL, NULL); + + term_paste_queue(term, FALSE); } void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, @@ -6052,47 +6125,7 @@ void term_nopaste(Terminal *term) { if (term->paste_len == 0) return; - sfree(term->paste_buffer); - term->paste_buffer = NULL; - term->paste_len = 0; -} - -int term_paste_pending(Terminal *term) -{ - return term->paste_len != 0; -} - -void term_paste(Terminal *term) -{ - long now, paste_diff; - - if (term->paste_len == 0) - return; - - /* Don't wait forever to paste */ - if (term->paste_hold) { - now = GETTICKCOUNT(); - paste_diff = now - term->last_paste; - if (paste_diff >= 0 && paste_diff < 450) - return; - } - term->paste_hold = 0; - - while (term->paste_pos < term->paste_len) { - int n = 0; - while (n + term->paste_pos < term->paste_len) { - if (term->paste_buffer[term->paste_pos + n++] == '\015') - break; - } - if (term->ldisc) - luni_send(term->ldisc, term->paste_buffer + term->paste_pos, n, 0); - term->paste_pos += n; - - if (term->paste_pos < term->paste_len) { - term->paste_hold = 1; - return; - } - } + expire_timer_context(&term->paste_timer_ctx); sfree(term->paste_buffer); term->paste_buffer = NULL; term->paste_len = 0; diff --git a/terminal.h b/terminal.h index 14e797b0..9f056112 100644 --- a/terminal.h +++ b/terminal.h @@ -222,8 +222,8 @@ struct terminal_tag { int attr_mask; wchar_t *paste_buffer; - int paste_len, paste_pos, paste_hold; - long last_paste; + int paste_len, paste_pos; + Terminal *paste_timer_ctx; void (*resize_fn)(void *, int, int); void *resize_ctx; diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 2b660739..f42761e1 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -93,7 +93,6 @@ struct gui_data { int ignore_sbar; int mouseptr_visible; int busy_status; - guint term_paste_idle_id; guint term_exit_idle_id; guint toplevel_callback_idle_id; int alt_keycode; @@ -1999,28 +1998,12 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata, term_do_paste(inst->term); - if (term_paste_pending(inst->term)) - inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst); - if (free_list_required) XFreeStringList(list); if (free_required) XFree(text); } -gint idle_paste_func(gpointer data) -{ - struct gui_data *inst = (struct gui_data *)data; - - if (term_paste_pending(inst->term)) - term_paste(inst->term); - else - gtk_idle_remove(inst->term_paste_idle_id); - - return TRUE; -} - - void get_clip(void *frontend, wchar_t ** p, int *len) { struct gui_data *inst = (struct gui_data *)frontend; diff --git a/windows/window.c b/windows/window.c index 15e77d72..7fb02fb3 100644 --- a/windows/window.c +++ b/windows/window.c @@ -877,8 +877,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg))) DispatchMessage(&msg); - /* Send the paste buffer if there's anything to send */ - term_paste(term); if (must_close_session) close_session();