]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Revamp the terminal paste mechanism using toplevel callbacks.
authorSimon Tatham <anakin@pobox.com>
Sat, 17 Aug 2013 16:06:12 +0000 (16:06 +0000)
committerSimon Tatham <anakin@pobox.com>
Sat, 17 Aug 2013 16:06:12 +0000 (16:06 +0000)
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.

[originally from svn r10020]

putty.h
terminal.c
terminal.h
unix/gtkwin.c
windows/window.c

diff --git a/putty.h b/putty.h
index d1a5daeb6a37bd2468463aabdf07686c1e215067..7fbb75ec57b98280b9fca1bfa05fa182db8aa0ee 100644 (file)
--- 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 *);
index 113edc4fc60e346f8f8d9d698bb083c6a39b14f4..e7095e0c2db2cf7e7a9e248e699de64ceb944e9d 100644 (file)
@@ -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;
index 14e797b09c421b7231e99f7d24798daa57712f74..9f0561124c0bfff58ab0534ba882ce94182caba1 100644 (file)
@@ -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;
index 2b66073954398bc7465da4db2d2f81d1caa47bb6..f42761e1159e93354bb13327317737c7938fa2a2 100644 (file)
@@ -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;
index 15e77d7236138dee621717ef6f5c8367ab765b3b..7fb02fb34e324df95093e131a823305186a98592 100644 (file)
@@ -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();