]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - terminal.c
Revamp the terminal paste mechanism using toplevel callbacks.
[PuTTY.git] / terminal.c
index febd897f46ff45282fef909446d343f698acdc69..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)
 {
@@ -1224,6 +1227,8 @@ static void power_on(Terminal *term, int clear)
     term->alt_which = 0;
     term_print_finish(term);
     term->xterm_mouse = 0;
+    term->xterm_extended_mouse = 0;
+    term->urxvt_extended_mouse = 0;
     set_raw_mouse_mode(term->frontend, FALSE);
     term->bracketed_paste = FALSE;
     {
@@ -1522,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;
@@ -2491,6 +2495,12 @@ static void toggle_mode(Terminal *term, int mode, int query, int state)
            term->xterm_mouse = state ? 2 : 0;
            set_raw_mouse_mode(term->frontend, state);
            break;
+         case 1006:                   /* xterm extended mouse */
+           term->xterm_extended_mouse = state ? 1 : 0;
+           break;
+         case 1015:                   /* urxvt extended mouse */
+           term->urxvt_extended_mouse = state ? 1 : 0;
+           break;
          case 1047:                   /* alternate screen */
            compatibility(OTHER);
            deselect(term);
@@ -2959,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)
@@ -2990,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;
@@ -5626,7 +5636,8 @@ static pos sel_spread_half(Terminal *term, pos p, int dir)
                    else
                        break;
                } else {
-                   if (ldata->lattr & LATTR_WRAPPED) {
+                   if (p.y+1 < term->rows && 
+                        (ldata->lattr & LATTR_WRAPPED)) {
                        termline *ldata2;
                        ldata2 = lineptr(p.y+1);
                        if (wordtype(term, UCSGET(ldata2->chars, 0))
@@ -5697,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;
@@ -5710,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) {
@@ -5753,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,
@@ -5817,25 +5899,26 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
     if (raw_mouse &&
        (term->selstate != ABOUT_TO) && (term->selstate != DRAGGING)) {
        int encstate = 0, r, c;
-       char abuf[16];
+       char abuf[32];
+       int len = 0;
 
        if (term->ldisc) {
 
            switch (braw) {
              case MBT_LEFT:
-               encstate = 0x20;               /* left button down */
+               encstate = 0x00;               /* left button down */
                break;
              case MBT_MIDDLE:
-               encstate = 0x21;
+               encstate = 0x01;
                break;
              case MBT_RIGHT:
-               encstate = 0x22;
+               encstate = 0x02;
                break;
              case MBT_WHEEL_UP:
-               encstate = 0x60;
+               encstate = 0x40;
                break;
              case MBT_WHEEL_DOWN:
-               encstate = 0x61;
+               encstate = 0x41;
                break;
              default: break;          /* placate gcc warning about enum use */
            }
@@ -5846,7 +5929,9 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
                encstate += 0x20;
                break;
              case MA_RELEASE:
-               encstate = 0x23;
+               /* If multiple extensions are enabled, the xterm 1006 is used, so it's okay to check for only that */
+               if (!term->xterm_extended_mouse)
+                   encstate = 0x03;
                term->mouse_is_down = 0;
                break;
              case MA_CLICK:
@@ -5860,11 +5945,18 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
                encstate += 0x04;
            if (ctrl)
                encstate += 0x10;
-           r = y + 33;
-           c = x + 33;
-
-           sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
-           ldisc_send(term->ldisc, abuf, 6, 0);
+           r = y + 1;
+           c = x + 1;
+
+           /* Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. */
+           if (term->xterm_extended_mouse) {
+               len = sprintf(abuf, "\033[<%d;%d;%d%c", encstate, c, r, a == MA_RELEASE ? 'm' : 'M');
+           } else if (term->urxvt_extended_mouse) {
+               len = sprintf(abuf, "\033[%d;%d;%dM", encstate + 32, c, r);
+           } 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);
        }
        return;
     }
@@ -5983,6 +6075,13 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked,
        request_paste(term->frontend);
     }
 
+    /*
+     * Since terminal output is suppressed during drag-selects, we
+     * should make sure to write any pending output if one has just
+     * finished.
+     */
+    if (term->selstate != DRAGGING)
+        term_out(term);
     term_update(term);
 }
 
@@ -6026,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;
@@ -6082,6 +6141,14 @@ void term_deselect(Terminal *term)
 {
     deselect(term);
     term_update(term);
+
+    /*
+     * Since terminal output is suppressed during drag-selects, we
+     * should make sure to write any pending output if one has just
+     * finished.
+     */
+    if (term->selstate != DRAGGING)
+        term_out(term);
 }
 
 int term_ldisc(Terminal *term, int option)