Begin destabilisation in the wake of 0.53! This checkin contains the
[u/mdw/putty] / terminal.c
index 64b5685..b286efe 100644 (file)
@@ -1,5 +1,3 @@
-#include <windows.h>
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -9,6 +7,8 @@
 #include "putty.h"
 #include "tree234.h"
 
+#define VT52_PLUS
+
 #define CL_ANSIMIN     0x0001         /* Codes in all ANSI like terminals. */
 #define CL_VT100       0x0002         /* VT100 */
 #define CL_VT100AVO    0x0004         /* VT100 +AVO; 132x24 (not 132x14) & attrs */
@@ -54,21 +54,20 @@ static int disptop;                /* distance scrolled back (0 or -ve) */
 static unsigned long *cpos;           /* cursor position (convenience) */
 
 static unsigned long *disptext;               /* buffer of text on real screen */
-static unsigned long *wanttext;               /* buffer of text we want on screen */
+static unsigned long *dispcurs;               /* location of cursor on real screen */
+static unsigned long curstype;        /* type of cursor on real screen */
 
-#define VBELL_TIMEOUT 100             /* millisecond len of visual bell */
+#define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */
 
 struct beeptime {
     struct beeptime *next;
-    long ticks;
+    unsigned long ticks;
 };
 static struct beeptime *beephead, *beeptail;
 int nbeeps;
 int beep_overloaded;
 long lastbeep;
 
-static unsigned char *selspace;               /* buffer for building selections in */
-
 #define TSIZE (sizeof(unsigned long))
 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
 
@@ -81,10 +80,15 @@ typedef struct {
 #define poslt(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x < (p2).x ) )
 #define posle(p1,p2) ( (p1).y < (p2).y || ( (p1).y == (p2).y && (p1).x <= (p2).x ) )
 #define poseq(p1,p2) ( (p1).y == (p2).y && (p1).x == (p2).x )
-#define posdiff(p1,p2) ( ((p2).y - (p1).y) * (cols+1) + (p2).x - (p1).x )
+#define posdiff(p1,p2) ( ((p1).y - (p2).y) * (cols+1) + (p1).x - (p2).x )
 #define incpos(p) ( (p).x == cols ? ((p).x = 0, (p).y++, 1) : ((p).x++, 0) )
 #define decpos(p) ( (p).x == 0 ? ((p).x = cols, (p).y--, 1) : ((p).x--, 0) )
 
+/* Product-order comparisons for rectangular block selection. */
+#define posPlt(p1,p2) ( (p1).y <= (p2).y && (p1).x < (p2).x )
+#define posPle(p1,p2) ( (p1).y <= (p2).y && (p1).x <= (p2).x )
+
+static bufchain inbuf;                /* terminal input buffer */
 static pos curs;                      /* cursor */
 static pos savecurs;                  /* saved cursor position */
 static int marg_t, marg_b;            /* scroll margins */
@@ -93,8 +97,9 @@ static int wrap, wrapnext;           /* wrap flags */
 static int insert;                    /* insert-mode flag */
 static int cset;                      /* 0 or 1: which char set */
 static int save_cset, save_csattr;     /* saved with cursor position */
+static int save_utf, save_wnext;       /* saved with cursor position */
 static int rvideo;                    /* global reverse video flag */
-static int rvbell_timeout;            /* for ESC[?5hESC[?5l vbell */
+static unsigned long rvbell_startpoint;/* for ESC[?5hESC[?5l vbell */
 static int cursor_on;                 /* cursor enabled flag */
 static int reset_132;                 /* Flag ESC c resets to 80 cols */
 static int use_bce;                   /* Use Background coloured erase */
@@ -103,6 +108,15 @@ static int tblinker;                      /* When the blinking text is on */
 static int blink_is_real;             /* Actually blink blinking text */
 static int term_echoing;              /* Does terminal want local echo? */
 static int term_editing;              /* Does terminal want local edit? */
+static int sco_acs, save_sco_acs;      /* CSI 10,11,12m -> OEM charset */
+static int vt52_bold;                 /* Force bold on non-bold colours */
+static int utf_state;                 /* Is there a pending UTF-8 character */
+static int utf_char;                  /* and what is it so far. */
+static int utf_size;                  /* The size of the UTF character. */
+static int printing, only_printing;    /* Are we doing ANSI printing? */
+static int print_state;                       /* state of print-end-sequence scan */
+static bufchain printer_buf;          /* buffered data for printer */
+static printer_job *print_job;
 
 static int xterm_mouse;                       /* send mouse messages to app */
 
@@ -111,7 +125,7 @@ static unsigned long cset_attr[2];
 /*
  * Saved settings on the alternate screen.
  */
-static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
+static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset, alt_sco_acs, alt_utf;
 static int alt_t, alt_b;
 static int alt_which;
 
@@ -142,20 +156,22 @@ static enum {
 
     DO_CTRLS,
 
-    IGNORE_NEXT,
-    SET_GL, SET_GR,
     SEEN_OSC_P,
     OSC_STRING, OSC_MAYBE_ST,
-    SEEN_ESCHASH,
     VT52_ESC,
     VT52_Y1,
-    VT52_Y2
+    VT52_Y2,
+    VT52_FG,
+    VT52_BG
 } termstate;
 
 static enum {
     NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
 } selstate;
 static enum {
+    LEXICOGRAPHIC, RECTANGULAR
+} seltype;
+static enum {
     SM_CHAR, SM_WORD, SM_LINE
 } selmode;
 static pos selstart, selend, selanchor;
@@ -179,8 +195,9 @@ static short wordness[256] = {
     2, 2, 2, 2, 2, 2, 2, 2,           /* EF */
 };
 
-static unsigned char sel_nl[] = SEL_NL;
-static char *paste_buffer = 0;
+#define sel_nl_sz  (sizeof(sel_nl)/sizeof(wchar_t))
+static wchar_t sel_nl[] = SEL_NL;
+static wchar_t *paste_buffer = 0;
 static int paste_len, paste_pos, paste_hold;
 
 /*
@@ -191,9 +208,32 @@ static void erase_lots(int, int, int);
 static void swap_screen(int);
 static void update_sbar(void);
 static void deselect(void);
-/* log session to file stuff ... */
-static FILE *lgfp = NULL;
-static void logtraffic(unsigned char c, int logmode);
+static void term_print_finish(void);
+
+/*
+ * Resize a line to make it `cols' columns wide.
+ */
+unsigned long *resizeline(unsigned long *line, int cols)
+{
+    int i, oldlen;
+    unsigned long lineattrs;
+
+    if (line[0] != (unsigned long)cols) {
+       /*
+        * This line is the wrong length, which probably means it
+        * hasn't been accessed since a resize. Resize it now.
+        */
+       oldlen = line[0];
+       lineattrs = line[oldlen + 1];
+       line = srealloc(line, TSIZE * (2 + cols));
+       line[0] = cols;
+       for (i = oldlen; i < cols; i++)
+           line[i + 1] = ERASE_CHAR;
+       line[cols + 1] = lineattrs & LATTR_MODE;
+    }
+
+    return line;
+}
 
 /*
  * Retrieve a line of the screen or of the scrollback, according to
@@ -202,9 +242,9 @@ static void logtraffic(unsigned char c, int logmode);
  */
 unsigned long *lineptr(int y, int lineno)
 {
-    unsigned long *line, lineattrs;
+    unsigned long *line, *newline;
     tree234 *whichtree;
-    int i, treeindex, oldlen;
+    int treeindex;
 
     if (y >= 0) {
        whichtree = screen;
@@ -218,20 +258,11 @@ unsigned long *lineptr(int y, int lineno)
     /* We assume that we don't screw up and retrieve something out of range. */
     assert(line != NULL);
 
-    if (line[0] != cols) {
-       /*
-        * This line is the wrong length, which probably means it
-        * hasn't been accessed since a resize. Resize it now.
-        */
-       oldlen = line[0];
-       lineattrs = line[oldlen + 1];
+    newline = resizeline(line, cols);
+    if (newline != line) {
        delpos234(whichtree, treeindex);
-       line = srealloc(line, TSIZE * (2 + cols));
-       line[0] = cols;
-       for (i = oldlen; i < cols; i++)
-           line[i + 1] = ERASE_CHAR;
-       line[cols + 1] = lineattrs & LATTR_MODE;
-       addpos234(whichtree, line, treeindex);
+       addpos234(whichtree, newline, treeindex);
+        line = newline;
     }
 
     return line + 1;
@@ -258,19 +289,23 @@ static void power_on(void)
     alt_wnext = wrapnext = alt_ins = insert = FALSE;
     alt_wrap = wrap = cfg.wrap_mode;
     alt_cset = cset = 0;
+    alt_utf = utf = 0;
+    alt_sco_acs = sco_acs = 0;
     cset_attr[0] = cset_attr[1] = ATTR_ASCII;
     rvideo = 0;
     in_vbell = FALSE;
     cursor_on = 1;
+    big_cursor = 0;
     save_attr = curr_attr = ATTR_DEFAULT;
     term_editing = term_echoing = FALSE;
-    ldisc_send(NULL, 0);              /* cause ldisc to notice changes */
+    ldisc_send(NULL, 0, 0);           /* cause ldisc to notice changes */
     app_cursor_keys = cfg.app_cursor;
     app_keypad_keys = cfg.app_keypad;
     use_bce = cfg.bce;
     blink_is_real = cfg.blinktext;
     erase_char = ERASE_CHAR;
     alt_which = 0;
+    term_print_finish();
     {
        int i;
        for (i = 0; i < 256; i++)
@@ -292,12 +327,15 @@ void term_update(void)
     Context ctx;
     ctx = get_ctx();
     if (ctx) {
+       int need_sbar_update = seen_disp_event;
        if ((seen_key_event && (cfg.scroll_on_key)) ||
            (seen_disp_event && (cfg.scroll_on_disp))) {
            disptop = 0;               /* return to main screen */
            seen_disp_event = seen_key_event = 0;
-           update_sbar();
+           need_sbar_update = TRUE;
        }
+       if (need_sbar_update)
+           update_sbar();
        do_paint(ctx, TRUE);
        sys_cursor(curs.x, curs.y - disptop);
        free_ctx(ctx);
@@ -317,6 +355,30 @@ void term_pwron(void)
 }
 
 /*
+ * When the user reconfigures us, we need to check the forbidden-
+ * alternate-screen config option, disable raw mouse mode if the
+ * user has disabled mouse reporting, and abandon a print job if
+ * the user has disabled printing.
+ */
+void term_reconfig(void)
+{
+    if (cfg.no_alt_screen)
+       swap_screen(0);
+    if (cfg.no_mouse_rep) {
+       xterm_mouse = 0;
+       set_raw_mouse_mode(0);
+    }
+    if (cfg.no_remote_charset) {
+       cset_attr[0] = cset_attr[1] = ATTR_ASCII;
+       sco_acs = alt_sco_acs = 0;
+       utf = 0;
+    }
+    if (!*cfg.printer) {
+       term_print_finish();
+    }
+}
+
+/*
  * Clear the scrollback.
  */
 void term_clrsb(void)
@@ -336,9 +398,8 @@ void term_init(void)
 {
     screen = alt_screen = scrollback = NULL;
     disptop = 0;
-    disptext = wanttext = NULL;
+    disptext = dispcurs = NULL;
     tabs = NULL;
-    selspace = NULL;
     deselect();
     rows = cols = -1;
     power_on();
@@ -353,9 +414,9 @@ void term_init(void)
  */
 void term_size(int newrows, int newcols, int newsavelines)
 {
-    tree234 *newsb, *newscreen, *newalt;
-    unsigned long *newdisp, *newwant, *oldline, *line;
-    int i, j, ccols;
+    tree234 *newalt;
+    unsigned long *newdisp, *line;
+    int i, j;
     int sblen;
     int save_alt_which = alt_which;
 
@@ -426,12 +487,7 @@ void term_size(int newrows, int newcols, int newsavelines)
        newdisp[i] = ATTR_INVALID;
     sfree(disptext);
     disptext = newdisp;
-
-    newwant = smalloc(newrows * (newcols + 1) * TSIZE);
-    for (i = 0; i < newrows * (newcols + 1); i++)
-       newwant[i] = ATTR_INVALID;
-    sfree(wanttext);
-    wanttext = newwant;
+    dispcurs = NULL;
 
     newalt = newtree234(NULL);
     for (i = 0; i < newrows; i++) {
@@ -448,10 +504,6 @@ void term_size(int newrows, int newcols, int newsavelines)
     }
     alt_screen = newalt;
 
-    sfree(selspace);
-    selspace =
-       smalloc((newrows + newsavelines) * (newcols + sizeof(sel_nl)));
-
     tabs = srealloc(tabs, newcols * sizeof(*tabs));
     {
        int i;
@@ -479,6 +531,7 @@ void term_size(int newrows, int newcols, int newsavelines)
 
     update_sbar();
     term_update();
+    back->size();
 }
 
 /*
@@ -524,6 +577,12 @@ static void swap_screen(int which)
     t = cset;
     cset = alt_cset;
     alt_cset = t;
+    t = utf;
+    utf = alt_utf;
+    alt_utf = t;
+    t = sco_acs;
+    sco_acs = alt_sco_acs;
+    alt_sco_acs = t;
 
     fix_cpos;
 }
@@ -563,7 +622,7 @@ static void check_selection(pos from, pos to)
 static void scroll(int topline, int botline, int lines, int sb)
 {
     unsigned long *line, *line2;
-    int i;
+    int i, seltop;
 
     if (topline != 0 || alt_which != 0)
        sb = FALSE;
@@ -571,6 +630,7 @@ static void scroll(int topline, int botline, int lines, int sb)
     if (lines < 0) {
        while (lines < 0) {
            line = delpos234(screen, botline);
+            line = resizeline(line, cols);
            for (i = 0; i < cols; i++)
                line[i + 1] = erase_char;
            line[cols + 1] = 0;
@@ -612,26 +672,64 @@ static void scroll(int topline, int botline, int lines, int sb)
                }
                addpos234(scrollback, line, sblen);
                line = line2;
+
+               /*
+                * If the user is currently looking at part of the
+                * scrollback, and they haven't enabled any options
+                * that are going to reset the scrollback as a
+                * result of this movement, then the chances are
+                * they'd like to keep looking at the same line. So
+                * we move their viewpoint at the same rate as the
+                * scroll, at least until their viewpoint hits the
+                * top end of the scrollback buffer, at which point
+                * we don't have the choice any more.
+                * 
+                * Thanks to Jan Holmen Holsten for the idea and
+                * initial implementation.
+                */
+               if (disptop > -savelines && disptop < 0)
+                   disptop--;
            }
+            line = resizeline(line, cols);
            for (i = 0; i < cols; i++)
                line[i + 1] = erase_char;
            line[cols + 1] = 0;
            addpos234(screen, line, botline);
 
-           if (selstart.y >= topline && selstart.y <= botline) {
+           /*
+            * If the selection endpoints move into the scrollback,
+            * we keep them moving until they hit the top. However,
+            * of course, if the line _hasn't_ moved into the
+            * scrollback then we don't do this, and cut them off
+            * at the top of the scroll region.
+            * 
+            * This applies to selstart and selend (for an existing
+            * selection), and also selanchor (for one being
+            * selected as we speak).
+            */
+           seltop = sb ? -savelines : topline;
+
+           if (selstart.y >= seltop && selstart.y <= botline) {
                selstart.y--;
-               if (selstart.y < topline) {
-                   selstart.y = topline;
+               if (selstart.y < seltop) {
+                   selstart.y = seltop;
                    selstart.x = 0;
                }
            }
-           if (selend.y >= topline && selend.y <= botline) {
+           if (selend.y >= seltop && selend.y <= botline) {
                selend.y--;
-               if (selend.y < topline) {
-                   selend.y = topline;
+               if (selend.y < seltop) {
+                   selend.y = seltop;
                    selend.x = 0;
                }
            }
+           if (selanchor.y >= seltop && selanchor.y <= botline) {
+               selanchor.y--;
+               if (selanchor.y < seltop) {
+                   selanchor.y = seltop;
+                   selanchor.x = 0;
+               }
+           }
 
            lines--;
        }
@@ -675,7 +773,10 @@ static void save_cursor(int save)
        savecurs = curs;
        save_attr = curr_attr;
        save_cset = cset;
+       save_utf = utf;
+       save_wnext = wrapnext;
        save_csattr = cset_attr[cset];
+       save_sco_acs = sco_acs;
     } else {
        curs = savecurs;
        /* Make sure the window hasn't shrunk since the save */
@@ -686,10 +787,20 @@ static void save_cursor(int save)
 
        curr_attr = save_attr;
        cset = save_cset;
+       utf = save_utf;
+       wrapnext = save_wnext;
+       /*
+        * wrapnext might reset to False if the x position is no
+        * longer at the rightmost edge.
+        */
+       if (wrapnext && curs.x < cols-1)
+           wrapnext = FALSE;
        cset_attr[cset] = save_csattr;
+       sco_acs = save_sco_acs;
        fix_cpos;
        if (use_bce)
-           erase_char = (' ' | (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
+           erase_char = (' ' | ATTR_ASCII |
+                        (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
     }
 }
 
@@ -721,6 +832,7 @@ static void erase_lots(int line_only, int from_begin, int to_end)
     }
     if (!to_end) {
        end = curs;
+       incpos(end);
     }
     check_selection(start, end);
 
@@ -730,8 +842,8 @@ static void erase_lots(int line_only, int from_begin, int to_end)
 
     ldata = lineptr(start.y);
     while (poslt(start, end)) {
-       if (start.y == cols && !erase_lattr)
-           ldata[start.x] &= ~ATTR_WRAPPED;
+       if (start.x == cols && !erase_lattr)
+           ldata[start.x] &= ~LATTR_WRAPPED;
        else
            ldata[start.x] = erase_char;
        if (incpos(start) && start.y < rows)
@@ -775,7 +887,7 @@ static void insch(int n)
  */
 static void toggle_mode(int mode, int query, int state)
 {
-    long ticks;
+    unsigned long ticks;
 
     if (query)
        switch (mode) {
@@ -784,10 +896,17 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 2:                      /* VT52 mode */
            vt52_mode = !state;
+           if (vt52_mode) {
+               blink_is_real = FALSE;
+               vt52_bold = FALSE;
+           } else {
+               blink_is_real = cfg.blinktext;
+           }
            break;
          case 3:                      /* 80/132 columns */
            deselect();
-           request_resize(state ? 132 : 80, rows, 1);
+           if (!cfg.no_remote_resize)
+               request_resize(state ? 132 : 80, rows);
            reset_132 = state;
            break;
          case 5:                      /* reverse video */
@@ -797,15 +916,21 @@ static void toggle_mode(int mode, int query, int state)
             * effective visual bell, so that ESC[?5hESC[?5l will
             * always be an actually _visible_ visual bell.
             */
-           ticks = GetTickCount();
-           if (rvideo && !state &&    /* we're turning it off */
-               ticks < rvbell_timeout) {       /* and it's not long since it was turned on */
+           ticks = GETTICKCOUNT();
+           /* turn off a previous vbell to avoid inconsistencies */
+           if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
+               in_vbell = FALSE;
+           if (rvideo && !state &&    /* we're turning it off... */
+               (ticks - rvbell_startpoint) < VBELL_TIMEOUT) {  /* ...soon */
+               /* If there's no vbell timeout already, or this one lasts
+                * longer, replace vbell_timeout with ours. */
+               if (!in_vbell ||
+                   (rvbell_startpoint - vbell_startpoint < VBELL_TIMEOUT))
+                   vbell_startpoint = rvbell_startpoint;
                in_vbell = TRUE;       /* we may clear rvideo but we set in_vbell */
-               if (vbell_timeout < rvbell_timeout)     /* don't move vbell end forward */
-                   vbell_timeout = rvbell_timeout;     /* vbell end is at least then */
            } else if (!rvideo && state) {
                /* This is an ON, so we notice the time and save it. */
-               rvbell_timeout = ticks + VBELL_TIMEOUT;
+               rvbell_startpoint = ticks;
            }
            rvideo = state;
            seen_disp_event = TRUE;
@@ -823,7 +948,7 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 10:                     /* set local edit mode */
            term_editing = state;
-           ldisc_send(NULL, 0);       /* cause ldisc to notice changes */
+           ldisc_send(NULL, 0, 0);    /* cause ldisc to notice changes */
            break;
          case 25:                     /* enable/disable cursor */
            compatibility2(OTHER, VT220);
@@ -833,7 +958,7 @@ static void toggle_mode(int mode, int query, int state)
          case 47:                     /* alternate screen */
            compatibility(OTHER);
            deselect();
-           swap_screen(state);
+           swap_screen(cfg.no_alt_screen ? 0 : state);
            disptop = 0;
            break;
          case 1000:                   /* xterm mouse 1 */
@@ -852,11 +977,14 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 12:                     /* set echo mode */
            term_echoing = !state;
-           ldisc_send(NULL, 0);       /* cause ldisc to notice changes */
+           ldisc_send(NULL, 0, 0);    /* cause ldisc to notice changes */
            break;
          case 20:                     /* Return sends ... */
            cr_lf_return = state;
            break;
+         case 34:                     /* Make cursor BIG */
+           compatibility2(OTHER, VT220);
+           big_cursor = !state;
        }
 }
 
@@ -873,16 +1001,62 @@ static void do_osc(void)
        switch (esc_args[0]) {
          case 0:
          case 1:
-           set_icon(osc_string);
+           if (!cfg.no_remote_wintitle)
+               set_icon(osc_string);
            if (esc_args[0] == 1)
                break;
            /* fall through: parameter 0 means set both */
          case 2:
          case 21:
-           set_title(osc_string);
+           if (!cfg.no_remote_wintitle)
+               set_title(osc_string);
+           break;
+       }
+    }
+}
+
+/*
+ * ANSI printing routines.
+ */
+static void term_print_setup(void)
+{
+    bufchain_clear(&printer_buf);
+    print_job = printer_start_job(cfg.printer);
+}
+static void term_print_flush(void)
+{
+    void *data;
+    int len;
+    int size;
+    while ((size = bufchain_size(&printer_buf)) > 5) {
+       bufchain_prefix(&printer_buf, &data, &len);
+       if (len > size-5)
+           len = size-5;
+       printer_job_data(print_job, data, len);
+       bufchain_consume(&printer_buf, len);
+    }
+}
+static void term_print_finish(void)
+{
+    void *data;
+    int len, size;
+    char c;
+
+    term_print_flush();
+    while ((size = bufchain_size(&printer_buf)) > 0) {
+       bufchain_prefix(&printer_buf, &data, &len);
+       c = *(char *)data;
+       if (c == '\033' || c == '\233') {
+           bufchain_consume(&printer_buf, size);
            break;
+       } else {
+           printer_job_data(print_job, &c, 1);
+           bufchain_consume(&printer_buf, 1);
        }
     }
+    printer_finish_job(print_job);
+    print_job = NULL;
+    printing = only_printing = FALSE;
 }
 
 /*
@@ -892,23 +1066,225 @@ static void do_osc(void)
  */
 void term_out(void)
 {
-    int c, inbuf_reap;
-
-    for (inbuf_reap = 0; inbuf_reap < inbuf_head; inbuf_reap++) {
-       c = inbuf[inbuf_reap];
+    int c, unget;
+    unsigned char localbuf[256], *chars;
+    int nchars = 0;
+
+    unget = -1;
+
+    while (nchars > 0 || bufchain_size(&inbuf) > 0) {
+       if (unget == -1) {
+           if (nchars == 0) {
+               void *ret;
+               bufchain_prefix(&inbuf, &ret, &nchars);
+               if (nchars > sizeof(localbuf))
+                   nchars = sizeof(localbuf);
+               memcpy(localbuf, ret, nchars);
+               bufchain_consume(&inbuf, nchars);
+               chars = localbuf;
+               assert(chars != NULL);
+           }
+           c = *chars++;
+           nchars--;
 
-       /*
-        * Optionally log the session traffic to a file. Useful for
-        * debugging and possibly also useful for actual logging.
-        */
-       logtraffic((unsigned char) c, LGTYP_DEBUG);
+           /*
+            * Optionally log the session traffic to a file. Useful for
+            * debugging and possibly also useful for actual logging.
+            */
+           if (cfg.logtype == LGTYP_DEBUG)
+               logtraffic((unsigned char) c, LGTYP_DEBUG);
+       } else {
+           c = unget;
+           unget = -1;
+       }
 
        /* Note only VT220+ are 8-bit VT102 is seven bit, it shouldn't even
         * be able to display 8-bit characters, but I'll let that go 'cause
         * of i18n.
         */
-       if (((c & 0x60) == 0 || c == '\177') &&
-           termstate < DO_CTRLS && ((c & 0x80) == 0 || has_compat(VT220))) {
+
+       /*
+        * If we're printing, add the character to the printer
+        * buffer.
+        */
+       if (printing) {
+           bufchain_add(&printer_buf, &c, 1);
+
+           /*
+            * If we're in print-only mode, we use a much simpler
+            * state machine designed only to recognise the ESC[4i
+            * termination sequence.
+            */
+           if (only_printing) {
+               if (c == '\033')
+                   print_state = 1;
+               else if (c == (unsigned char)'\233')
+                   print_state = 2;
+               else if (c == '[' && print_state == 1)
+                   print_state = 2;
+               else if (c == '4' && print_state == 2)
+                   print_state = 3;
+               else if (c == 'i' && print_state == 3)
+                   print_state = 4;
+               else
+                   print_state = 0;
+               if (print_state == 4) {
+                   printing = only_printing = FALSE;
+                   term_print_finish();
+               }
+               continue;
+           }
+       }
+
+       /* First see about all those translations. */
+       if (termstate == TOPLEVEL) {
+           if (in_utf)
+               switch (utf_state) {
+                 case 0:
+                   if (c < 0x80) {
+                       /* UTF-8 must be stateless so we ignore iso2022. */
+                       if (unitab_ctrl[c] != 0xFF) 
+                            c = unitab_ctrl[c];
+                       else c = ((unsigned char)c) | ATTR_ASCII;
+                       break;
+                   } else if ((c & 0xe0) == 0xc0) {
+                       utf_size = utf_state = 1;
+                       utf_char = (c & 0x1f);
+                   } else if ((c & 0xf0) == 0xe0) {
+                       utf_size = utf_state = 2;
+                       utf_char = (c & 0x0f);
+                   } else if ((c & 0xf8) == 0xf0) {
+                       utf_size = utf_state = 3;
+                       utf_char = (c & 0x07);
+                   } else if ((c & 0xfc) == 0xf8) {
+                       utf_size = utf_state = 4;
+                       utf_char = (c & 0x03);
+                   } else if ((c & 0xfe) == 0xfc) {
+                       utf_size = utf_state = 5;
+                       utf_char = (c & 0x01);
+                   } else {
+                       c = UCSERR;
+                       break;
+                   }
+                   continue;
+                 case 1:
+                 case 2:
+                 case 3:
+                 case 4:
+                 case 5:
+                   if ((c & 0xC0) != 0x80) {
+                       unget = c;
+                       c = UCSERR;
+                       utf_state = 0;
+                       break;
+                   }
+                   utf_char = (utf_char << 6) | (c & 0x3f);
+                   if (--utf_state)
+                       continue;
+
+                   c = utf_char;
+
+                   /* Is somebody trying to be evil! */
+                   if (c < 0x80 ||
+                       (c < 0x800 && utf_size >= 2) ||
+                       (c < 0x10000 && utf_size >= 3) ||
+                       (c < 0x200000 && utf_size >= 4) ||
+                       (c < 0x4000000 && utf_size >= 5))
+                       c = UCSERR;
+
+                   /* Unicode line separator and paragraph separator are CR-LF */
+                   if (c == 0x2028 || c == 0x2029)
+                       c = 0x85;
+
+                   /* High controls are probably a Baaad idea too. */
+                   if (c < 0xA0)
+                       c = 0xFFFD;
+
+                   /* The UTF-16 surrogates are not nice either. */
+                   /*       The standard give the option of decoding these: 
+                    *       I don't want to! */
+                   if (c >= 0xD800 && c < 0xE000)
+                       c = UCSERR;
+
+                   /* ISO 10646 characters now limited to UTF-16 range. */
+                   if (c > 0x10FFFF)
+                       c = UCSERR;
+
+                   /* This is currently a TagPhobic application.. */
+                   if (c >= 0xE0000 && c <= 0xE007F)
+                       continue;
+
+                   /* U+FEFF is best seen as a null. */
+                   if (c == 0xFEFF)
+                       continue;
+                   /* But U+FFFE is an error. */
+                   if (c == 0xFFFE || c == 0xFFFF)
+                       c = UCSERR;
+
+                   /* Oops this is a 16bit implementation */
+                   if (c >= 0x10000)
+                       c = 0xFFFD;
+                   break;
+           }
+           /* Are we in the nasty ACS mode? Note: no sco in utf mode. */
+           else if(sco_acs && 
+                   (c!='\033' && c!='\n' && c!='\r' && c!='\b'))
+           {
+              if (sco_acs == 2) c ^= 0x80;
+              c |= ATTR_SCOACS;
+           } else {
+               switch (cset_attr[cset]) {
+                   /* 
+                    * Linedraw characters are different from 'ESC ( B'
+                    * only for a small range. For ones outside that
+                    * range, make sure we use the same font as well as
+                    * the same encoding.
+                    */
+                 case ATTR_LINEDRW:
+                   if (unitab_ctrl[c] != 0xFF)
+                       c = unitab_ctrl[c];
+                   else
+                       c = ((unsigned char) c) | ATTR_LINEDRW;
+                   break;
+
+                 case ATTR_GBCHR:
+                   /* If UK-ASCII, make the '#' a LineDraw Pound */
+                   if (c == '#') {
+                       c = '}' | ATTR_LINEDRW;
+                       break;
+                   }
+                 /*FALLTHROUGH*/ case ATTR_ASCII:
+                   if (unitab_ctrl[c] != 0xFF)
+                       c = unitab_ctrl[c];
+                   else
+                       c = ((unsigned char) c) | ATTR_ASCII;
+                   break;
+               case ATTR_SCOACS:
+                   if (c>=' ') c = ((unsigned char)c) | ATTR_SCOACS;
+                   break;
+               }
+           }
+       }
+
+       /* How about C1 controls ? */
+       if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
+           has_compat(VT220)) {
+           termstate = SEEN_ESC;
+           esc_query = FALSE;
+           c = '@' + (c & 0x1F);
+       }
+
+       /* Or the GL control. */
+       if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
+           if (curs.x && !wrapnext)
+               curs.x--;
+           wrapnext = FALSE;
+           fix_cpos;
+           if (!cfg.no_dbackspace)    /* destructive bksp might be disabled */
+               *cpos = (' ' | curr_attr | ATTR_ASCII);
+       } else
+           /* Or normal C0 controls. */
+       if ((c & -32) == 0 && termstate < DO_CTRLS) {
            switch (c) {
              case '\005':             /* terminal type query */
                /* Strictly speaking this is VT100 but a VT100 defaults to
@@ -936,17 +1312,17 @@ void term_out(void)
                        } else if (*s == '^') {
                            state = 1;
                        } else
-                           *d++ = xlat_kbd2tty((unsigned char) *s);
+                           *d++ = *s;
                    }
-                   ldisc_send(abuf, d - abuf);
+                   lpage_send(DEFAULT_CODEPAGE, abuf, d - abuf, 0);
                }
                break;
              case '\007':
                {
                    struct beeptime *newbeep;
-                   long ticks;
+                   unsigned long ticks;
 
-                   ticks = GetTickCount();
+                   ticks = GETTICKCOUNT();
 
                    if (!beep_overloaded) {
                        newbeep = smalloc(sizeof(struct beeptime));
@@ -975,7 +1351,7 @@ void term_out(void)
                    }
 
                    if (cfg.bellovl && beep_overloaded &&
-                       ticks - lastbeep >= cfg.bellovl_s) {
+                       ticks - lastbeep >= (unsigned)cfg.bellovl_s) {
                        /*
                         * If we're currently overloaded and the
                         * last beep was more than s seconds ago,
@@ -996,13 +1372,11 @@ void term_out(void)
                    /*
                     * Perform an actual beep if we're not overloaded.
                     */
-                   if ((!cfg.bellovl || !beep_overloaded)
-                       && cfg.beep != 0) {
-                       if (cfg.beep != 2)
-                           beep(cfg.beep);
-                       else if (cfg.beep == 2) {
+                   if (!cfg.bellovl || !beep_overloaded) {
+                       beep(cfg.beep);
+                       if (cfg.beep == BELL_VISUAL) {
                            in_vbell = TRUE;
-                           vbell_timeout = ticks + VBELL_TIMEOUT;
+                           vbell_startpoint = ticks;
                            term_update();
                        }
                    }
@@ -1010,7 +1384,7 @@ void term_out(void)
                }
                break;
              case '\b':
-               if (curs.x == 0 && curs.y == 0);
+               if (curs.x == 0 && (curs.y == 0 || wrap == 0));
                else if (curs.x == 0 && curs.y > 0)
                    curs.x = cols - 1, curs.y--;
                else if (wrapnext)
@@ -1034,20 +1408,9 @@ void term_out(void)
                else {
                    compatibility(ANSIMIN);
                    termstate = SEEN_ESC;
+                   esc_query = FALSE;
                }
                break;
-             case 0233:
-               compatibility(VT220);
-               termstate = SEEN_CSI;
-               esc_nargs = 1;
-               esc_args[0] = ARG_DEFAULT;
-               esc_query = FALSE;
-               break;
-             case 0235:
-               compatibility(VT220);
-               termstate = SEEN_OSC;
-               esc_args[0] = 0;
-               break;
              case '\r':
                curs.x = 0;
                wrapnext = FALSE;
@@ -1056,8 +1419,16 @@ void term_out(void)
                paste_hold = 0;
                logtraffic((unsigned char) c, LGTYP_ASCII);
                break;
-             case '\013':
              case '\014':
+               if (has_compat(SCOANSI)) {
+                   move(0, 0, 0);
+                   erase_lots(FALSE, FALSE, TRUE);
+                   disptop = 0;
+                   wrapnext = FALSE;
+                   seen_disp_event = 1;
+                   break;
+               }
+             case '\013':
                compatibility(VT100);
              case '\n':
                if (curs.y == marg_b)
@@ -1094,22 +1465,13 @@ void term_out(void)
                }
                seen_disp_event = TRUE;
                break;
-             case '\177':             /* Destructive backspace
-                                         This does nothing on a real VT100 */
-               compatibility(OTHER);
-               if (curs.x && !wrapnext)
-                   curs.x--;
-               wrapnext = FALSE;
-               fix_cpos;
-               *cpos = (' ' | curr_attr | ATTR_ASCII);
-               break;
            }
        } else
            switch (termstate) {
              case TOPLEVEL:
                /* Only graphic characters get this far, ctrls are stripped above */
                if (wrapnext && wrap) {
-                   cpos[1] |= ATTR_WRAPPED;
+                   cpos[1] |= LATTR_WRAPPED;
                    if (curs.y == marg_b)
                        scroll(marg_t, marg_b, 1, TRUE);
                    else if (curs.y < rows - 1)
@@ -1125,49 +1487,55 @@ void term_out(void)
                    incpos(cursplus);
                    check_selection(curs, cursplus);
                }
-               switch (cset_attr[cset]) {
-                   /*
-                    * Linedraw characters are different from 'ESC ( B'
-                    * only for a small range. For ones outside that
-                    * range, make sure we use the same font as well as
-                    * the same encoding.
-                    */
-                 case ATTR_LINEDRW:
-                   if (c < 0x5f || c > 0x7F)
-                       *cpos++ =
-                           xlat_tty2scr((unsigned char) c) | curr_attr |
-                           ATTR_ASCII;
-                   else if (c == 0x5F)
-                       *cpos++ = ' ' | curr_attr | ATTR_ASCII;
-                   else
-                       *cpos++ =
-                           ((unsigned char) c) | curr_attr | ATTR_LINEDRW;
-                   break;
-                 case ATTR_GBCHR:
-                   /* If UK-ASCII, make the '#' a LineDraw Pound */
-                   if (c == '#') {
-                       *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
+               if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
+                   logtraffic((unsigned char) c, LGTYP_ASCII);
+               {
+                   extern int wcwidth(wchar_t ucs);
+                   int width = 0;
+                   if (DIRECT_CHAR(c))
+                       width = 1;
+                   if (!width)
+                       width = wcwidth((wchar_t) c);
+                   switch (width) {
+                     case 2:
+                       *cpos++ = c | curr_attr;
+                       if (++curs.x == cols) {
+                           *cpos |= LATTR_WRAPPED;
+                           if (curs.y == marg_b)
+                               scroll(marg_t, marg_b, 1, TRUE);
+                           else if (curs.y < rows - 1)
+                               curs.y++;
+                           curs.x = 0;
+                           fix_cpos;
+                       }
+                       *cpos++ = UCSWIDE | curr_attr;
+                       break;
+                     case 1:
+                       *cpos++ = c | curr_attr;
                        break;
+                     default:
+                       continue;
                    }
-                 /*FALLTHROUGH*/ default:
-                   *cpos = xlat_tty2scr((unsigned char) c) | curr_attr |
-                       (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
-                   logtraffic((unsigned char) c, LGTYP_ASCII);
-                   cpos++;
-                   break;
                }
                curs.x++;
                if (curs.x == cols) {
                    cpos--;
                    curs.x--;
                    wrapnext = TRUE;
+                   if (wrap && vt52_mode) {
+                       cpos[1] |= LATTR_WRAPPED;
+                       if (curs.y == marg_b)
+                           scroll(marg_t, marg_b, 1, TRUE);
+                       else if (curs.y < rows - 1)
+                           curs.y++;
+                       curs.x = 0;
+                       fix_cpos;
+                       wrapnext = FALSE;
+                   }
                }
                seen_disp_event = 1;
                break;
 
-             case IGNORE_NEXT:
-               termstate = TOPLEVEL;
-               break;
              case OSC_MAYBE_ST:
                /*
                 * This state is virtually identical to SEEN_ESC, with the
@@ -1181,12 +1549,15 @@ void term_out(void)
                }
                /* else fall through */
              case SEEN_ESC:
-               termstate = TOPLEVEL;
-               switch (c) {
-                 case ' ':            /* some weird sequence? */
-                   compatibility(VT220);
-                   termstate = IGNORE_NEXT;
+               if (c >= ' ' && c <= '/') {
+                   if (esc_query)
+                       esc_query = -1;
+                   else
+                       esc_query = c;
                    break;
+               }
+               termstate = TOPLEVEL;
+               switch (ANSI(c, esc_query)) {
                  case '[':            /* enter CSI mode */
                    termstate = SEEN_CSI;
                    esc_nargs = 1;
@@ -1199,14 +1570,6 @@ void term_out(void)
                    termstate = SEEN_OSC;
                    esc_args[0] = 0;
                    break;
-                 case '(':            /* should set GL */
-                   compatibility(VT100);
-                   termstate = SET_GL;
-                   break;
-                 case ')':            /* should set GR */
-                   compatibility(VT100);
-                   termstate = SET_GR;
-                   break;
                  case '7':            /* save cursor */
                    compatibility(VT100);
                    save_cursor(TRUE);
@@ -1257,27 +1620,128 @@ void term_out(void)
                    break;
                  case 'Z':            /* terminal type query */
                    compatibility(VT100);
-                   ldisc_send(id_string, strlen(id_string));
+                   ldisc_send(id_string, strlen(id_string), 0);
                    break;
                  case 'c':            /* restore power-on settings */
                    compatibility(VT100);
                    power_on();
                    if (reset_132) {
-                       request_resize(80, rows, 1);
+                       if (!cfg.no_remote_resize)
+                           request_resize(80, rows);
                        reset_132 = 0;
                    }
                    fix_cpos;
                    disptop = 0;
                    seen_disp_event = TRUE;
                    break;
-                 case '#':            /* ESC # 8 fills screen with Es :-) */
-                   compatibility(VT100);
-                   termstate = SEEN_ESCHASH;
-                   break;
                  case 'H':            /* set a tab */
                    compatibility(VT100);
                    tabs[curs.x] = TRUE;
                    break;
+
+                 case ANSI('8', '#'):  /* ESC # 8 fills screen with Es :-) */
+                   compatibility(VT100);
+                   {
+                       unsigned long *ldata;
+                       int i, j;
+                       pos scrtop, scrbot;
+
+                       for (i = 0; i < rows; i++) {
+                           ldata = lineptr(i);
+                           for (j = 0; j < cols; j++)
+                               ldata[j] = ATTR_DEFAULT | 'E';
+                           ldata[cols] = 0;
+                       }
+                       disptop = 0;
+                       seen_disp_event = TRUE;
+                       scrtop.x = scrtop.y = 0;
+                       scrbot.x = 0;
+                       scrbot.y = rows;
+                       check_selection(scrtop, scrbot);
+                   }
+                   break;
+
+                 case ANSI('3', '#'):
+                 case ANSI('4', '#'):
+                 case ANSI('5', '#'):
+                 case ANSI('6', '#'):
+                   compatibility(VT100);
+                   {
+                       unsigned long nlattr;
+                       unsigned long *ldata;
+                       switch (ANSI(c, esc_query)) {
+                         case ANSI('3', '#'):
+                           nlattr = LATTR_TOP;
+                           break;
+                         case ANSI('4', '#'):
+                           nlattr = LATTR_BOT;
+                           break;
+                         case ANSI('5', '#'):
+                           nlattr = LATTR_NORM;
+                           break;
+                         default: /* spiritually case ANSI('6', '#'): */
+                           nlattr = LATTR_WIDE;
+                           break;
+                       }
+                       ldata = lineptr(curs.y);
+                       ldata[cols] &= ~LATTR_MODE;
+                       ldata[cols] |= nlattr;
+                   }
+                   break;
+
+                 case ANSI('A', '('):
+                   compatibility(VT100);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_GBCHR;
+                   break;
+                 case ANSI('B', '('):
+                   compatibility(VT100);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_ASCII;
+                   break;
+                 case ANSI('0', '('):
+                   compatibility(VT100);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_LINEDRW;
+                   break;
+                 case ANSI('U', '('): 
+                   compatibility(OTHER);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[0] = ATTR_SCOACS; 
+                   break;
+
+                 case ANSI('A', ')'):
+                   compatibility(VT100);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_GBCHR;
+                   break;
+                 case ANSI('B', ')'):
+                   compatibility(VT100);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_ASCII;
+                   break;
+                 case ANSI('0', ')'):
+                   compatibility(VT100);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_LINEDRW;
+                   break;
+                 case ANSI('U', ')'): 
+                   compatibility(OTHER);
+                   if (!cfg.no_remote_charset)
+                       cset_attr[1] = ATTR_SCOACS; 
+                   break;
+
+                 case ANSI('8', '%'):  /* Old Linux code */
+                 case ANSI('G', '%'):
+                   compatibility(OTHER);
+                   if (!cfg.no_remote_charset)
+                       utf = 1;
+                   break;
+                 case ANSI('@', '%'):
+                   compatibility(OTHER);
+                   if (!cfg.no_remote_charset)
+                       utf = 0;
+                   break;
                }
                break;
              case SEEN_CSI:
@@ -1310,18 +1774,20 @@ void term_out(void)
                        break;
                      case 'e':       /* move down N lines */
                        compatibility(ANSI);
+                       /* FALLTHROUGH */
                      case 'B':
                        move(curs.x, curs.y + def(esc_args[0], 1), 1);
                        seen_disp_event = TRUE;
                        break;
-                     case 'a':       /* move right N cols */
-                       compatibility(ANSI);
                      case ANSI('c', '>'):      /* report xterm version */
                        compatibility(OTHER);
                        /* this reports xterm version 136 so that VIM can
                           use the drag messages from the mouse reporting */
-                       ldisc_send("\033[>0;136;0c", 11);
+                       ldisc_send("\033[>0;136;0c", 11, 0);
                        break;
+                     case 'a':       /* move right N cols */
+                       compatibility(ANSI);
+                       /* FALLTHROUGH */
                      case 'C':
                        move(curs.x + def(esc_args[0], 1), curs.y, 1);
                        seen_disp_event = TRUE;
@@ -1413,16 +1879,16 @@ void term_out(void)
                      case 'c':       /* terminal type query */
                        compatibility(VT100);
                        /* This is the response for a VT102 */
-                       ldisc_send(id_string, strlen(id_string));
+                       ldisc_send(id_string, strlen(id_string), 0);
                        break;
                      case 'n':       /* cursor position query */
                        if (esc_args[0] == 6) {
                            char buf[32];
                            sprintf(buf, "\033[%d;%dR", curs.y + 1,
                                    curs.x + 1);
-                           ldisc_send(buf, strlen(buf));
+                           ldisc_send(buf, strlen(buf), 0);
                        } else if (esc_args[0] == 5) {
-                           ldisc_send("\033[0n", 4);
+                           ldisc_send("\033[0n", 4, 0);
                        }
                        break;
                      case 'h':       /* toggle modes to high */
@@ -1434,6 +1900,23 @@ void term_out(void)
                                toggle_mode(esc_args[i], esc_query, TRUE);
                        }
                        break;
+                     case 'i':
+                     case ANSI_QUE('i'):
+                       compatibility(VT100);
+                       {
+                           if (esc_nargs != 1) break;
+                           if (esc_args[0] == 5 && *cfg.printer) {
+                               printing = TRUE;
+                               only_printing = !esc_query;
+                               print_state = 0;
+                               term_print_setup();
+                           } else if (esc_args[0] == 4 && printing) {
+                               printing = FALSE;
+                               only_printing = FALSE;
+                               term_print_finish();
+                           }
+                       }
+                       break;                  
                      case 'l':       /* toggle modes to low */
                      case ANSI_QUE('l'):
                        compatibility(VT100);
@@ -1494,11 +1977,9 @@ void term_out(void)
                             * this was selected by CSI 7m.
                             *
                             * case 2:
-                            *  This is DIM on the VT100-AVO and VT102
-                            * case 5:
-                            *  This is BLINK on the VT100-AVO and VT102+
+                            *  This is sometimes DIM, eg on the GIGI and Linux
                             * case 8:
-                            *  This is INVIS on the VT100-AVO and VT102
+                            *  This is sometimes INVIS various ANSI.
                             * case 21:
                             *  This like 22 disables BOLD, DIM and INVIS
                             *
@@ -1534,6 +2015,18 @@ void term_out(void)
                                  case 7:       /* enable reverse video */
                                    curr_attr |= ATTR_REVERSE;
                                    break;
+                                 case 10:      /* SCO acs off */
+                                   compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
+                                   sco_acs = 0; break;
+                                 case 11:      /* SCO acs on */
+                                   compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
+                                   sco_acs = 1; break;
+                                 case 12:      /* SCO acs on flipped */
+                                   compatibility(SCOANSI);
+                                   if (cfg.no_remote_charset) break;
+                                   sco_acs = 2; break;
                                  case 22:      /* disable bold */
                                    compatibility2(OTHER, VT220);
                                    curr_attr &= ~ATTR_BOLD;
@@ -1587,11 +2080,9 @@ void term_out(void)
                                }
                            }
                            if (use_bce)
-                               erase_char =
-                                   (' ' |
-                                    (curr_attr &
-                                     (ATTR_FGMASK | ATTR_BGMASK |
-                                      ATTR_BLINK)));
+                               erase_char = (' ' | ATTR_ASCII |
+                                            (curr_attr & 
+                                             (ATTR_FGMASK | ATTR_BGMASK)));
                        }
                        break;
                      case 's':       /* save cursor */
@@ -1608,13 +2099,126 @@ void term_out(void)
                         * illegal values (eg first arg 1..9) for window changing 
                         * and reports.
                         */
-                       compatibility(VT340TEXT);
                        if (esc_nargs <= 1
                            && (esc_args[0] < 1 || esc_args[0] >= 24)) {
-                           request_resize(cols, def(esc_args[0], 24), 0);
+                           compatibility(VT340TEXT);
+                           if (!cfg.no_remote_resize)
+                               request_resize(cols, def(esc_args[0], 24));
                            deselect();
+                       } else if (esc_nargs >= 1 &&
+                                  esc_args[0] >= 1 &&
+                                  esc_args[0] < 24) {
+                           compatibility(OTHER);
+
+                           switch (esc_args[0]) {
+                               int x, y, len;
+                               char buf[80], *p;
+                             case 1:
+                               set_iconic(FALSE);
+                               break;
+                             case 2:
+                               set_iconic(TRUE);
+                               break;
+                             case 3:
+                               if (esc_nargs >= 3) {
+                                   if (!cfg.no_remote_resize)
+                                       move_window(def(esc_args[1], 0),
+                                                   def(esc_args[2], 0));
+                               }
+                               break;
+                             case 4:
+                               /* We should resize the window to a given
+                                * size in pixels here, but currently our
+                                * resizing code isn't healthy enough to
+                                * manage it. */
+                               break;
+                             case 5:
+                               set_zorder(TRUE);   /* move to top */
+                               break;
+                             case 6:
+                               set_zorder(FALSE);  /* move to bottom */
+                               break;
+                             case 7:
+                               refresh_window();
+                               break;
+                             case 8:
+                               if (esc_nargs >= 3) {
+                                   if (!cfg.no_remote_resize)
+                                       request_resize(def(esc_args[2], cfg.width),
+                                                      def(esc_args[1], cfg.height));
+                               }
+                               break;
+                             case 9:
+                               if (esc_nargs >= 2)
+                                   set_zoomed(esc_args[1] ? TRUE : FALSE);
+                               break;
+                             case 11:
+                               ldisc_send(is_iconic() ? "\033[1t" : "\033[2t",
+                                          4, 0);
+                               break;
+                             case 13:
+                               get_window_pos(&x, &y);
+                               len = sprintf(buf, "\033[3;%d;%dt", x, y);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 14:
+                               get_window_pixels(&x, &y);
+                               len = sprintf(buf, "\033[4;%d;%dt", x, y);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 18:
+                               len = sprintf(buf, "\033[8;%d;%dt",
+                                             rows, cols);
+                               ldisc_send(buf, len, 0);
+                               break;
+                             case 19:
+                               /*
+                                * Hmmm. Strictly speaking we
+                                * should return `the size of the
+                                * screen in characters', but
+                                * that's not easy: (a) window
+                                * furniture being what it is it's
+                                * hard to compute, and (b) in
+                                * resize-font mode maximising the
+                                * window wouldn't change the
+                                * number of characters. *shrug*. I
+                                * think we'll ignore it for the
+                                * moment and see if anyone
+                                * complains, and then ask them
+                                * what they would like it to do.
+                                */
+                               break;
+                             case 20:
+                               p = get_window_title(TRUE);
+                               len = strlen(p);
+                               ldisc_send("\033]L", 3, 0);
+                               ldisc_send(p, len, 0);
+                               ldisc_send("\033\\", 2, 0);
+                               break;
+                             case 21:
+                               p = get_window_title(FALSE);
+                               len = strlen(p);
+                               ldisc_send("\033]l", 3, 0);
+                               ldisc_send(p, len, 0);
+                               ldisc_send("\033\\", 2, 0);
+                               break;
+                           }
                        }
                        break;
+                     case 'S':
+                       compatibility(SCOANSI);
+                       scroll(marg_t, marg_b, def(esc_args[0], 1), TRUE);
+                       fix_cpos;
+                       wrapnext = FALSE;
+                       seen_disp_event = TRUE;
+                       break;
+                     case 'T':
+                       compatibility(SCOANSI);
+                       scroll(marg_t, marg_b, -def(esc_args[0], 1), TRUE);
+                       fix_cpos;
+                       wrapnext = FALSE;
+                       seen_disp_event = TRUE;
+                       break;
                      case ANSI('|', '*'):
                        /* VT420 sequence DECSNLS
                         * Set number of lines on screen
@@ -1623,9 +2227,8 @@ void term_out(void)
                         */
                        compatibility(VT420);
                        if (esc_nargs == 1 && esc_args[0] > 0) {
-                           request_resize(cols,
-                                          def(esc_args[0], cfg.height),
-                                          0);
+                           if (!cfg.no_remote_resize)
+                               request_resize(cols, def(esc_args[0], cfg.height));
                            deselect();
                        }
                        break;
@@ -1636,8 +2239,8 @@ void term_out(void)
                         */
                        compatibility(VT340TEXT);
                        if (esc_nargs <= 1) {
-                           request_resize(def(esc_args[0], cfg.width),
-                                          rows, 0);
+                           if (!cfg.no_remote_resize)
+                               request_resize(def(esc_args[0], cfg.width), rows);
                            deselect();
                        }
                        break;
@@ -1666,19 +2269,33 @@ void term_out(void)
                            if (i == 0 || i == 1) {
                                strcpy(buf, "\033[2;1;1;112;112;1;0x");
                                buf[2] += i;
-                               ldisc_send(buf, 20);
+                               ldisc_send(buf, 20, 0);
                            }
                        }
                        break;
+                     case 'Z':         /* BackTab for xterm */
+                       compatibility(OTHER);
+                       {
+                           int i = def(esc_args[0], 1);
+                           pos old_curs = curs;
+
+                           for(;i>0 && curs.x>0; i--) {
+                               do {
+                                   curs.x--;
+                               } while (curs.x >0 && !tabs[curs.x]);
+                           }
+                           fix_cpos;
+                           check_selection(old_curs, curs);
+                       }
+                       break;
                      case ANSI('L', '='):
                        compatibility(OTHER);
                        use_bce = (esc_args[0] <= 0);
                        erase_char = ERASE_CHAR;
                        if (use_bce)
-                           erase_char =
-                               (' ' |
-                                (curr_attr &
-                                 (ATTR_FGMASK | ATTR_BGMASK)));
+                           erase_char = (' ' | ATTR_ASCII |
+                                        (curr_attr & 
+                                         (ATTR_FGMASK | ATTR_BGMASK)));
                        break;
                      case ANSI('E', '='):
                        compatibility(OTHER);
@@ -1689,7 +2306,7 @@ void term_out(void)
                         * This first appeared in the VT220, but we do need to get 
                         * back to PuTTY mode so I won't check it.
                         *
-                        * The arg in 40..42 are a PuTTY extension.
+                        * The arg in 40..42,50 are a PuTTY extension.
                         * The 2nd arg, 8bit vs 7bit is not checked.
                         *
                         * Setting VT102 mode should also change the Fkeys to
@@ -1750,33 +2367,17 @@ void term_out(void)
                         * Well we should do a soft reset at this point ...
                         */
                        if (!has_compat(VT420) && has_compat(VT100)) {
-                           if (reset_132)
-                               request_resize(132, 24, 1);
-                           else
-                               request_resize(80, 24, 1);
+                           if (!cfg.no_remote_resize) {
+                               if (reset_132)
+                                   request_resize(132, 24);
+                               else
+                                   request_resize(80, 24);
+                           }
                        }
 #endif
                        break;
                    }
                break;
-             case SET_GL:
-             case SET_GR:
-               /* VT100 only here, checked above */
-               switch (c) {
-                 case 'A':
-                   cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
-                   break;
-                 case '0':
-                   cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
-                   break;
-                 case 'B':
-                 default:             /* specifically, 'B' */
-                   cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
-                   break;
-               }
-               if (!has_compat(VT220) || c != '%')
-                   termstate = TOPLEVEL;
-               break;
              case SEEN_OSC:
                osc_w = FALSE;
                switch (c) {
@@ -1858,8 +2459,10 @@ void term_out(void)
                        val = c - 'A' + 10;
                    else if (c >= 'a' && c <= 'a' + max - 10)
                        val = c - 'a' + 10;
-                   else
+                   else {
                        termstate = TOPLEVEL;
+                       break;
+                   }
                    osc_string[osc_strlen++] = val;
                    if (osc_strlen >= 7) {
                        palette_set(osc_string[0],
@@ -1890,55 +2493,6 @@ void term_out(void)
                    osc_strlen = 0;
                }
                break;
-             case SEEN_ESCHASH:
-               {
-                   unsigned long nlattr;
-                   unsigned long *ldata;
-                   int i, j;
-                   pos scrtop, scrbot;
-
-                   switch (c) {
-                     case '8':
-                       for (i = 0; i < rows; i++) {
-                           ldata = lineptr(i);
-                           for (j = 0; j < cols; j++)
-                               ldata[j] = ATTR_DEFAULT | 'E';
-                           ldata[cols] = 0;
-                       }
-                       disptop = 0;
-                       seen_disp_event = TRUE;
-                       scrtop.x = scrtop.y = 0;
-                       scrbot.x = 0;
-                       scrbot.y = rows;
-                       check_selection(scrtop, scrbot);
-                       break;
-
-                     case '3':
-                     case '4':
-                     case '5':
-                     case '6':
-                       switch (c) {
-                         case '3':
-                           nlattr = LATTR_TOP;
-                           break;
-                         case '4':
-                           nlattr = LATTR_BOT;
-                           break;
-                         case '5':
-                           nlattr = LATTR_NORM;
-                           break;
-                         case '6':
-                           nlattr = LATTR_WIDE;
-                           break;
-                       }
-
-                       ldata = lineptr(curs.y);
-                       ldata[cols] &= ~LATTR_MODE;
-                       ldata[cols] |= nlattr;
-                   }
-               }
-               termstate = TOPLEVEL;
-               break;
              case VT52_ESC:
                termstate = TOPLEVEL;
                seen_disp_event = TRUE;
@@ -1955,6 +2509,46 @@ void term_out(void)
                  case 'D':
                    move(curs.x - 1, curs.y, 1);
                    break;
+                   /*
+                    * From the VT100 Manual
+                    * NOTE: The special graphics characters in the VT100
+                    *       are different from those in the VT52
+                    *
+                    * From VT102 manual:
+                    *       137 _  Blank             - Same
+                    *       140 `  Reserved          - Humm.
+                    *       141 a  Solid rectangle   - Similar
+                    *       142 b  1/                - Top half of fraction for the
+                    *       143 c  3/                - subscript numbers below.
+                    *       144 d  5/
+                    *       145 e  7/
+                    *       146 f  Degrees           - Same
+                    *       147 g  Plus or minus     - Same
+                    *       150 h  Right arrow
+                    *       151 i  Ellipsis (dots)
+                    *       152 j  Divide by
+                    *       153 k  Down arrow
+                    *       154 l  Bar at scan 0
+                    *       155 m  Bar at scan 1
+                    *       156 n  Bar at scan 2
+                    *       157 o  Bar at scan 3     - Similar
+                    *       160 p  Bar at scan 4     - Similar
+                    *       161 q  Bar at scan 5     - Similar
+                    *       162 r  Bar at scan 6     - Same
+                    *       163 s  Bar at scan 7     - Similar
+                    *       164 t  Subscript 0
+                    *       165 u  Subscript 1
+                    *       166 v  Subscript 2
+                    *       167 w  Subscript 3
+                    *       170 x  Subscript 4
+                    *       171 y  Subscript 5
+                    *       172 z  Subscript 6
+                    *       173 {  Subscript 7
+                    *       174 |  Subscript 8
+                    *       175 }  Subscript 9
+                    *       176 ~  Paragraph
+                    *
+                    */
                  case 'F':
                    cset_attr[cset = 0] = ATTR_LINEDRW;
                    break;
@@ -1979,6 +2573,7 @@ void term_out(void)
                  case 'K':
                    erase_lots(TRUE, FALSE, TRUE);
                    break;
+#if 0
                  case 'V':
                    /* XXX Print cursor line */
                    break;
@@ -1988,11 +2583,12 @@ void term_out(void)
                  case 'X':
                    /* XXX Stop controller mode */
                    break;
+#endif
                  case 'Y':
                    termstate = VT52_Y1;
                    break;
                  case 'Z':
-                   ldisc_send("\033/Z", 3);
+                   ldisc_send("\033/Z", 3, 0);
                    break;
                  case '=':
                    app_keypad_keys = TRUE;
@@ -2006,7 +2602,9 @@ void term_out(void)
                     *     emulation.
                     */
                    vt52_mode = FALSE;
+                   blink_is_real = cfg.blinktext;
                    break;
+#if 0
                  case '^':
                    /* XXX Enter auto print mode */
                    break;
@@ -2016,6 +2614,104 @@ void term_out(void)
                  case ']':
                    /* XXX Print screen */
                    break;
+#endif
+
+#ifdef VT52_PLUS
+                 case 'E':
+                   /* compatibility(ATARI) */
+                   move(0, 0, 0);
+                   erase_lots(FALSE, FALSE, TRUE);
+                   disptop = 0;
+                   break;
+                 case 'L':
+                   /* compatibility(ATARI) */
+                   if (curs.y <= marg_b)
+                       scroll(curs.y, marg_b, -1, FALSE);
+                   break;
+                 case 'M':
+                   /* compatibility(ATARI) */
+                   if (curs.y <= marg_b)
+                       scroll(curs.y, marg_b, 1, TRUE);
+                   break;
+                 case 'b':
+                   /* compatibility(ATARI) */
+                   termstate = VT52_FG;
+                   break;
+                 case 'c':
+                   /* compatibility(ATARI) */
+                   termstate = VT52_BG;
+                   break;
+                 case 'd':
+                   /* compatibility(ATARI) */
+                   erase_lots(FALSE, TRUE, FALSE);
+                   disptop = 0;
+                   break;
+                 case 'e':
+                   /* compatibility(ATARI) */
+                   cursor_on = TRUE;
+                   break;
+                 case 'f':
+                   /* compatibility(ATARI) */
+                   cursor_on = FALSE;
+                   break;
+                   /* case 'j': Save cursor position - broken on ST */
+                   /* case 'k': Restore cursor position */
+                 case 'l':
+                   /* compatibility(ATARI) */
+                   erase_lots(TRUE, TRUE, TRUE);
+                   curs.x = 0;
+                   wrapnext = FALSE;
+                   fix_cpos;
+                   break;
+                 case 'o':
+                   /* compatibility(ATARI) */
+                   erase_lots(TRUE, TRUE, FALSE);
+                   break;
+                 case 'p':
+                   /* compatibility(ATARI) */
+                   curr_attr |= ATTR_REVERSE;
+                   break;
+                 case 'q':
+                   /* compatibility(ATARI) */
+                   curr_attr &= ~ATTR_REVERSE;
+                   break;
+                 case 'v':            /* wrap Autowrap on - Wyse style */
+                   /* compatibility(ATARI) */
+                   wrap = 1;
+                   break;
+                 case 'w':            /* Autowrap off */
+                   /* compatibility(ATARI) */
+                   wrap = 0;
+                   break;
+
+                 case 'R':
+                   /* compatibility(OTHER) */
+                   vt52_bold = FALSE;
+                   curr_attr = ATTR_DEFAULT;
+                   if (use_bce)
+                       erase_char = (' ' | ATTR_ASCII |
+                                    (curr_attr & 
+                                     (ATTR_FGMASK | ATTR_BGMASK)));
+                   break;
+                 case 'S':
+                   /* compatibility(VI50) */
+                   curr_attr |= ATTR_UNDER;
+                   break;
+                 case 'W':
+                   /* compatibility(VI50) */
+                   curr_attr &= ~ATTR_UNDER;
+                   break;
+                 case 'U':
+                   /* compatibility(VI50) */
+                   vt52_bold = TRUE;
+                   curr_attr |= ATTR_BOLD;
+                   break;
+                 case 'T':
+                   /* compatibility(VI50) */
+                   vt52_bold = FALSE;
+                   curr_attr &= ~ATTR_BOLD;
+                   break;
+#endif
                }
                break;
              case VT52_Y1:
@@ -2026,6 +2722,36 @@ void term_out(void)
                termstate = TOPLEVEL;
                move(c - ' ', curs.y, 0);
                break;
+
+#ifdef VT52_PLUS
+             case VT52_FG:
+               termstate = TOPLEVEL;
+               curr_attr &= ~ATTR_FGMASK;
+               curr_attr &= ~ATTR_BOLD;
+               curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
+               if ((c & 0x8) || vt52_bold)
+                   curr_attr |= ATTR_BOLD;
+
+               if (use_bce)
+                   erase_char = (' ' | ATTR_ASCII |
+                                (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
+               break;
+             case VT52_BG:
+               termstate = TOPLEVEL;
+               curr_attr &= ~ATTR_BGMASK;
+               curr_attr &= ~ATTR_BLINK;
+               curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
+
+               /* Note: bold background */
+               if (c & 0x8)
+                   curr_attr |= ATTR_BLINK;
+
+               if (use_bce)
+                   erase_char = (' ' | ATTR_ASCII |
+                                (curr_attr & (ATTR_FGMASK | ATTR_BGMASK)));
+               break;
+#endif
+             default: break;          /* placate gcc warning about enum use */
            }
        if (selstate != NO_SELECTION) {
            pos cursplus = curs;
@@ -2033,9 +2759,11 @@ void term_out(void)
            check_selection(curs, cursplus);
        }
     }
-    inbuf_head = 0;
+
+    term_print_flush();
 }
 
+#if 0
 /*
  * Compare two lines to determine whether they are sufficiently
  * alike to scroll-optimise one to the other. Return the degree of
@@ -2049,6 +2777,7 @@ static int linecmp(unsigned long *a, unsigned long *b)
        n += (*a++ == *b++);
     return n;
 }
+#endif
 
 /*
  * Given a context, update the window. Out of paranoia, we don't
@@ -2056,98 +2785,198 @@ static int linecmp(unsigned long *a, unsigned long *b)
  */
 static void do_paint(Context ctx, int may_optimise)
 {
-    int i, j, start, our_curs_y;
-    unsigned long attr, rv, cursor;
+    int i, j, our_curs_y;
+    unsigned long rv, cursor;
     pos scrpos;
     char ch[1024];
-    long ticks;
+    long cursor_background = ERASE_CHAR;
+    unsigned long ticks;
 
     /*
      * Check the visual bell state.
      */
     if (in_vbell) {
-       ticks = GetTickCount();
-       if (ticks - vbell_timeout >= 0)
-           in_vbell = FALSE;
-    }
+       ticks = GETTICKCOUNT();
+       if (ticks - vbell_startpoint >= VBELL_TIMEOUT)
+           in_vbell = FALSE; 
+   }
+
+    rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
 
     /* Depends on:
      * screen array, disptop, scrtop,
      * selection, rv, 
      * cfg.blinkpc, blink_is_real, tblinker, 
-     * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
+     * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
      */
+
+    /* Has the cursor position or type changed ? */
     if (cursor_on) {
        if (has_focus) {
            if (blinker || !cfg.blink_cur)
-               cursor = ATTR_ACTCURS;
+               cursor = TATTR_ACTCURS;
            else
                cursor = 0;
        } else
-           cursor = ATTR_PASCURS;
+           cursor = TATTR_PASCURS;
        if (wrapnext)
-           cursor |= ATTR_RIGHTCURS;
+           cursor |= TATTR_RIGHTCURS;
     } else
        cursor = 0;
-    rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
     our_curs_y = curs.y - disptop;
 
+    if (dispcurs && (curstype != cursor ||
+                    dispcurs !=
+                    disptext + our_curs_y * (cols + 1) + curs.x)) {
+       if (dispcurs > disptext && 
+               (*dispcurs & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+           dispcurs[-1] |= ATTR_INVALID;
+       if ( (dispcurs[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+           dispcurs[1] |= ATTR_INVALID;
+       *dispcurs |= ATTR_INVALID;
+       curstype = 0;
+    }
+    dispcurs = NULL;
+
+    /* The normal screen data */
     for (i = 0; i < rows; i++) {
        unsigned long *ldata;
        int lattr;
+       int idx, dirty_line, dirty_run, selected;
+       unsigned long attr = 0;
+       int updated_line = 0;
+       int start = 0;
+       int ccount = 0;
+       int last_run_dirty = 0;
+
        scrpos.y = i + disptop;
        ldata = lineptr(scrpos.y);
        lattr = (ldata[cols] & LATTR_MODE);
-       for (j = 0; j <= cols; j++) {
-           unsigned long d = ldata[j];
-           int idx = i * (cols + 1) + j;
+
+       idx = i * (cols + 1);
+       dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
+       disptext[idx + cols] = ldata[cols];
+
+       for (j = 0; j < cols; j++, idx++) {
+           unsigned long tattr, tchar;
+           unsigned long *d = ldata + j;
+           int break_run;
            scrpos.x = j;
 
-           wanttext[idx] = lattr | (((d & ~ATTR_WRAPPED) ^ rv
-                                     ^ (posle(selstart, scrpos) &&
-                                        poslt(scrpos, selend) ?
-                                        ATTR_REVERSE : 0)) |
-                                    (i == our_curs_y
-                                     && j == curs.x ? cursor : 0));
-           if (blink_is_real) {
-               if (has_focus && tblinker && (wanttext[idx] & ATTR_BLINK)) {
-                   wanttext[idx] &= ATTR_MASK;
-                   wanttext[idx] += ' ';
+           tchar = (*d & (CHAR_MASK | CSET_MASK));
+           tattr = (*d & (ATTR_MASK ^ CSET_MASK));
+           switch (tchar & CSET_MASK) {
+             case ATTR_ASCII:
+               tchar = unitab_line[tchar & 0xFF];
+               break;
+             case ATTR_LINEDRW:
+               tchar = unitab_xterm[tchar & 0xFF];
+               break;
+             case ATTR_SCOACS:  
+               tchar = unitab_scoacs[tchar&0xFF]; 
+               break;
+           }
+           tattr |= (tchar & CSET_MASK);
+           tchar &= CHAR_MASK;
+           if ((d[1] & (CHAR_MASK | CSET_MASK)) == UCSWIDE)
+                   tattr |= ATTR_WIDE;
+
+           /* Video reversing things */
+           if (seltype == LEXICOGRAPHIC)
+               selected = posle(selstart, scrpos) && poslt(scrpos, selend);
+           else
+               selected = posPle(selstart, scrpos) && posPlt(scrpos, selend);
+           tattr = (tattr ^ rv
+                    ^ (selected ? ATTR_REVERSE : 0));
+
+           /* 'Real' blinking ? */
+           if (blink_is_real && (tattr & ATTR_BLINK)) {
+               if (has_focus && tblinker) {
+                   tchar = ' ';
+                   tattr &= ~CSET_MASK;
+                   tattr |= ATTR_ACP;
                }
-               wanttext[idx] &= ~ATTR_BLINK;
+               tattr &= ~ATTR_BLINK;
            }
-       }
-    }
 
-    /*
-     * We would perform scrolling optimisations in here, if they
-     * didn't have a nasty tendency to cause the whole sodding
-     * program to hang for a second at speed-critical moments.
-     * We'll leave it well alone...
-     */
+           /*
+            * Check the font we'll _probably_ be using to see if 
+            * the character is wide when we don't want it to be.
+            */
+           if ((tchar | tattr) != (disptext[idx]& ~ATTR_NARROW)) {
+               if ((tattr & ATTR_WIDE) == 0 && 
+                   CharWidth(ctx, (tchar | tattr) & 0xFFFF) == 2)
+                   tattr |= ATTR_NARROW;
+           } else if (disptext[idx]&ATTR_NARROW)
+               tattr |= ATTR_NARROW;
+
+           /* Cursor here ? Save the 'background' */
+           if (i == our_curs_y && j == curs.x) {
+               cursor_background = tattr | tchar;
+               dispcurs = disptext + idx;
+           }
 
-    for (i = 0; i < rows; i++) {
-       int idx = i * (cols + 1);
-       int lattr = (wanttext[idx + cols] & LATTR_MODE);
-       start = -1;
-       for (j = 0; j <= cols; j++, idx++) {
-           unsigned long t = wanttext[idx];
-           int needs_update = (j < cols && t != disptext[idx]);
-           int keep_going = (start != -1 && needs_update &&
-                             (t & ATTR_MASK) == attr &&
-                             j - start < sizeof(ch));
-           if (start != -1 && !keep_going) {
-               do_text(ctx, start, i, ch, j - start, attr, lattr);
-               start = -1;
+           if ((disptext[idx] ^ tattr) & ATTR_WIDE)
+               dirty_line = TRUE;
+
+           break_run = (tattr != attr || j - start >= sizeof(ch));
+
+           /* Special hack for VT100 Linedraw glyphs */
+           if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
+               && tchar <= 0xBD) break_run = TRUE;
+
+           if (!dbcs_screenfont && !dirty_line) {
+               if ((tchar | tattr) == disptext[idx])
+                   break_run = TRUE;
+               else if (!dirty_run && ccount == 1)
+                   break_run = TRUE;
+           }
+
+           if (break_run) {
+               if ((dirty_run || last_run_dirty) && ccount > 0) {
+                   do_text(ctx, start, i, ch, ccount, attr, lattr);
+                   updated_line = 1;
+               }
+               start = j;
+               ccount = 0;
+               attr = tattr;
+               if (dbcs_screenfont)
+                   last_run_dirty = dirty_run;
+               dirty_run = dirty_line;
            }
-           if (needs_update) {
-               if (start == -1) {
-                   start = j;
-                   attr = t & ATTR_MASK;
+
+           if ((tchar | tattr) != disptext[idx])
+               dirty_run = TRUE;
+           ch[ccount++] = (char) tchar;
+           disptext[idx] = tchar | tattr;
+
+           /* If it's a wide char step along to the next one. */
+           if (tattr & ATTR_WIDE) {
+               if (++j < cols) {
+                   idx++;
+                   d++;
+                   /* Cursor is here ? Ouch! */
+                   if (i == our_curs_y && j == curs.x) {
+                       cursor_background = *d;
+                       dispcurs = disptext + idx;
+                   }
+                   if (disptext[idx] != *d)
+                       dirty_run = TRUE;
+                   disptext[idx] = *d;
                }
-               ch[j - start] = (char) (t & CHAR_MASK);
            }
-           disptext[idx] = t;
+       }
+       if (dirty_run && ccount > 0) {
+           do_text(ctx, start, i, ch, ccount, attr, lattr);
+           updated_line = 1;
+       }
+
+       /* Cursor on this line ? (and changed) */
+       if (i == our_curs_y && (curstype != cursor || updated_line)) {
+           ch[0] = (char) (cursor_background & CHAR_MASK);
+           attr = (cursor_background & ATTR_MASK) | cursor;
+           do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
+           curstype = cursor;
        }
     }
 }
@@ -2162,7 +2991,7 @@ void term_blink(int flg)
     static long last_tblink = 0;
     long now, blink_diff;
 
-    now = GetTickCount();
+    now = GETTICKCOUNT();
     blink_diff = now - last_tblink;
 
     /* Make sure the text blinks no more than 2Hz */
@@ -2179,8 +3008,8 @@ void term_blink(int flg)
 
     blink_diff = now - last_blink;
 
-    /* Make sure the cursor blinks no faster than GetCaretBlinkTime() */
-    if (blink_diff >= 0 && blink_diff < (long) GetCaretBlinkTime())
+    /* Make sure the cursor blinks no faster than system blink rate */
+    if (blink_diff >= 0 && blink_diff < (long) CURSORBLINK)
        return;
 
     last_blink = now;
@@ -2201,14 +3030,14 @@ void term_invalidate(void)
 /*
  * Paint the window in response to a WM_PAINT message.
  */
-void term_paint(Context ctx, int l, int t, int r, int b)
+void term_paint(Context ctx, int left, int top, int right, int bottom)
 {
-    int i, j, left, top, right, bottom;
+    int i, j;
+    if (left < 0) left = 0;
+    if (top < 0) top = 0;
+    if (right >= cols) right = cols-1;
+    if (bottom >= rows) bottom = rows-1;
 
-    left = l / font_width;
-    right = (r - 1) / font_width;
-    top = t / font_height;
-    bottom = (b - 1) / font_height;
     for (i = top; i <= bottom && i < rows; i++) {
        if ((disptext[i * (cols + 1) + cols] & LATTR_MODE) == LATTR_NORM)
            for (j = left; j <= right && j < cols; j++)
@@ -2221,8 +3050,9 @@ void term_paint(Context ctx, int l, int t, int r, int b)
     /* This should happen soon enough, also for some reason it sometimes 
      * fails to actually do anything when re-sizing ... painting the wrong
      * window perhaps ?
-     do_paint (ctx, FALSE);
      */
+    if (alt_pressed)
+        do_paint (ctx, FALSE);
 }
 
 /*
@@ -2245,87 +3075,275 @@ void term_scroll(int rel, int where)
     term_update();
 }
 
-static void clipme(pos top, pos bottom, char *workbuf)
+static void clipme(pos top, pos bottom, int rect)
 {
-    char *wbptr;                      /* where next char goes within workbuf */
+    wchar_t *workbuf;
+    wchar_t *wbptr;                   /* where next char goes within workbuf */
+    int old_top_x;
     int wblen = 0;                    /* workbuf len */
     int buflen;                               /* amount of memory allocated to workbuf */
 
-    if (workbuf != NULL) {            /* user supplied buffer? */
-       buflen = -1;                   /* assume buffer passed in is big enough */
-       wbptr = workbuf;               /* start filling here */
-    } else
-       buflen = 0;                    /* No data is available yet */
+    buflen = 5120;                    /* Default size */
+    workbuf = smalloc(buflen * sizeof(wchar_t));
+    wbptr = workbuf;                  /* start filling here */
+    old_top_x = top.x;                /* needed for rect==1 */
 
     while (poslt(top, bottom)) {
        int nl = FALSE;
        unsigned long *ldata = lineptr(top.y);
        pos nlpos;
 
+       /*
+        * nlpos will point at the maximum position on this line we
+        * should copy up to. So we start it at the end of the
+        * line...
+        */
        nlpos.y = top.y;
        nlpos.x = cols;
 
-       if (!(ldata[cols] & ATTR_WRAPPED)) {
-           while ((ldata[nlpos.x - 1] & CHAR_MASK) == 0x20
-                  && poslt(top, nlpos)) decpos(nlpos);
+       /*
+        * ... move it backwards if there's unused space at the end
+        * of the line (and also set `nl' if this is the case,
+        * because in normal selection mode this means we need a
+        * newline at the end)...
+        */
+       if (!(ldata[cols] & LATTR_WRAPPED)) {
+           while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
+                   (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
+                    (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
+                  && poslt(top, nlpos))
+               decpos(nlpos);
            if (poslt(nlpos, bottom))
                nl = TRUE;
        }
+
+       /*
+        * ... and then clip it to the terminal x coordinate if
+        * we're doing rectangular selection. (In this case we
+        * still did the above, so that copying e.g. the right-hand
+        * column from a table doesn't fill with spaces on the
+        * right.)
+        */
+       if (rect) {
+           if (nlpos.x > bottom.x)
+               nlpos.x = bottom.x;
+           nl = (top.y < bottom.y);
+       }
+
        while (poslt(top, bottom) && poslt(top, nlpos)) {
-           int ch = (ldata[top.x] & CHAR_MASK);
-           int set = (ldata[top.x] & CSET_MASK);
-
-           /* VT Specials -> ISO8859-1 for Cut&Paste */
-           static const unsigned char poorman2[] =
-               "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
-
-           if (set && !cfg.rawcnp) {
-               if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
-                   int x;
-                   if ((x = poorman2[2 * (ch - 0x60) + 1]) == ' ')
-                       x = 0;
-                   ch = (x << 8) + poorman2[2 * (ch - 0x60)];
+#if 0
+           char cbuf[16], *p;
+           sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
+#else
+           wchar_t cbuf[16], *p;
+           int uc = (ldata[top.x] & 0xFFFF);
+           int set, c;
+
+           if (uc == UCSWIDE) {
+               top.x++;
+               continue;
+           }
+
+           switch (uc & CSET_MASK) {
+             case ATTR_LINEDRW:
+               if (!cfg.rawcnp) {
+                   uc = unitab_xterm[uc & 0xFF];
+                   break;
                }
+             case ATTR_ASCII:
+               uc = unitab_line[uc & 0xFF];
+               break;
+             case ATTR_SCOACS:  
+               uc = unitab_scoacs[uc&0xFF]; 
+               break;
+           }
+           switch (uc & CSET_MASK) {
+             case ATTR_ACP:
+               uc = unitab_font[uc & 0xFF];
+               break;
+             case ATTR_OEMCP:
+               uc = unitab_oemcp[uc & 0xFF];
+               break;
            }
 
-           while (ch != 0) {
-               if (cfg.rawcnp || !!(ch & 0xE0)) {
-                   if (wblen == buflen) {
-                       workbuf = srealloc(workbuf, buflen += 100);
-                       wbptr = workbuf + wblen;
+           set = (uc & CSET_MASK);
+           c = (uc & CHAR_MASK);
+           cbuf[0] = uc;
+           cbuf[1] = 0;
+
+           if (DIRECT_FONT(uc)) {
+               if (c >= ' ' && c != 0x7F) {
+                   unsigned char buf[4];
+                   WCHAR wbuf[4];
+                   int rv;
+                   if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
+                       buf[0] = c;
+                       buf[1] = (unsigned char) ldata[top.x + 1];
+                       rv = MultiByteToWideChar(font_codepage,
+                                                0, buf, 2, wbuf, 4);
+                       top.x++;
+                   } else {
+                       buf[0] = c;
+                       rv = MultiByteToWideChar(font_codepage,
+                                                0, buf, 1, wbuf, 4);
                    }
-                   wblen++;
-                   *wbptr++ = (unsigned char) ch;
+
+                   if (rv > 0) {
+                       memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
+                       cbuf[rv] = 0;
+                   }
+               }
+           }
+#endif
+
+           for (p = cbuf; *p; p++) {
+               /* Enough overhead for trailing NL and nul */
+               if (wblen >= buflen - 16) {
+                   workbuf =
+                       srealloc(workbuf,
+                                sizeof(wchar_t) * (buflen += 100));
+                   wbptr = workbuf + wblen;
                }
-               ch >>= 8;
+               wblen++;
+               *wbptr++ = *p;
            }
            top.x++;
        }
        if (nl) {
            int i;
-           for (i = 0; i < sizeof(sel_nl); i++) {
-               if (wblen == buflen) {
-                   workbuf = srealloc(workbuf, buflen += 100);
-                   wbptr = workbuf + wblen;
-               }
+           for (i = 0; i < sel_nl_sz; i++) {
                wblen++;
                *wbptr++ = sel_nl[i];
            }
        }
        top.y++;
-       top.x = 0;
+       top.x = rect ? old_top_x : 0;
     }
+    wblen++;
+    *wbptr++ = 0;
     write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
     if (buflen > 0)                   /* indicates we allocated this buffer */
        sfree(workbuf);
-
 }
+
 void term_copyall(void)
 {
     pos top;
     top.y = -count234(scrollback);
     top.x = 0;
-    clipme(top, curs, NULL /* dynamic allocation */ );
+    clipme(top, curs, 0);
+}
+
+/*
+ * The wordness array is mainly for deciding the disposition of the US-ASCII 
+ * characters.
+ */
+static int wordtype(int uc)
+{
+    static struct {
+       int start, end, ctype;
+    } *wptr, ucs_words[] = {
+       {
+       128, 160, 0}, {
+       161, 191, 1}, {
+       215, 215, 1}, {
+       247, 247, 1}, {
+       0x037e, 0x037e, 1},            /* Greek question mark */
+       {
+       0x0387, 0x0387, 1},            /* Greek ano teleia */
+       {
+       0x055a, 0x055f, 1},            /* Armenian punctuation */
+       {
+       0x0589, 0x0589, 1},            /* Armenian full stop */
+       {
+       0x0700, 0x070d, 1},            /* Syriac punctuation */
+       {
+       0x104a, 0x104f, 1},            /* Myanmar punctuation */
+       {
+       0x10fb, 0x10fb, 1},            /* Georgian punctuation */
+       {
+       0x1361, 0x1368, 1},            /* Ethiopic punctuation */
+       {
+       0x166d, 0x166e, 1},            /* Canadian Syl. punctuation */
+       {
+       0x17d4, 0x17dc, 1},            /* Khmer punctuation */
+       {
+       0x1800, 0x180a, 1},            /* Mongolian punctuation */
+       {
+       0x2000, 0x200a, 0},            /* Various spaces */
+       {
+       0x2070, 0x207f, 2},            /* superscript */
+       {
+       0x2080, 0x208f, 2},            /* subscript */
+       {
+       0x200b, 0x27ff, 1},            /* punctuation and symbols */
+       {
+       0x3000, 0x3000, 0},            /* ideographic space */
+       {
+       0x3001, 0x3020, 1},            /* ideographic punctuation */
+       {
+       0x303f, 0x309f, 3},            /* Hiragana */
+       {
+       0x30a0, 0x30ff, 3},            /* Katakana */
+       {
+       0x3300, 0x9fff, 3},            /* CJK Ideographs */
+       {
+       0xac00, 0xd7a3, 3},            /* Hangul Syllables */
+       {
+       0xf900, 0xfaff, 3},            /* CJK Ideographs */
+       {
+       0xfe30, 0xfe6b, 1},            /* punctuation forms */
+       {
+       0xff00, 0xff0f, 1},            /* half/fullwidth ASCII */
+       {
+       0xff1a, 0xff20, 1},            /* half/fullwidth ASCII */
+       {
+       0xff3b, 0xff40, 1},            /* half/fullwidth ASCII */
+       {
+       0xff5b, 0xff64, 1},            /* half/fullwidth ASCII */
+       {
+       0xfff0, 0xffff, 0},            /* half/fullwidth ASCII */
+       {
+       0, 0, 0}
+    };
+
+    uc &= (CSET_MASK | CHAR_MASK);
+
+    switch (uc & CSET_MASK) {
+      case ATTR_LINEDRW:
+       uc = unitab_xterm[uc & 0xFF];
+       break;
+      case ATTR_ASCII:
+       uc = unitab_line[uc & 0xFF];
+       break;
+      case ATTR_SCOACS:  
+       uc = unitab_scoacs[uc&0xFF]; 
+       break;
+    }
+    switch (uc & CSET_MASK) {
+      case ATTR_ACP:
+       uc = unitab_font[uc & 0xFF];
+       break;
+      case ATTR_OEMCP:
+       uc = unitab_oemcp[uc & 0xFF];
+       break;
+    }
+
+    /* For DBCS font's I can't do anything usefull. Even this will sometimes
+     * fail as there's such a thing as a double width space. :-(
+     */
+    if (dbcs_screenfont && font_codepage == line_codepage)
+       return (uc != ' ');
+
+    if (uc < 0x80)
+       return wordness[uc];
+
+    for (wptr = ucs_words; wptr->start; wptr++) {
+       if (uc >= wptr->start && uc <= wptr->end)
+           return wptr->ctype;
+    }
+
+    return 2;
 }
 
 /*
@@ -2335,6 +3353,7 @@ static pos sel_spread_half(pos p, int dir)
 {
     unsigned long *ldata;
     short wvalue;
+    int topy = -count234(scrollback);
 
     ldata = lineptr(p.y);
 
@@ -2344,7 +3363,7 @@ static pos sel_spread_half(pos p, int dir)
         * In this mode, every character is a separate unit, except
         * for runs of spaces at the end of a non-wrapping line.
         */
-       if (!(ldata[cols] & ATTR_WRAPPED)) {
+       if (!(ldata[cols] & LATTR_WRAPPED)) {
            unsigned long *q = ldata + cols;
            while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
                q--;
@@ -2359,15 +3378,49 @@ static pos sel_spread_half(pos p, int dir)
         * In this mode, the units are maximal runs of characters
         * whose `wordness' has the same value.
         */
-       wvalue = wordness[ldata[p.x] & CHAR_MASK];
+       wvalue = wordtype(ldata[p.x]);
        if (dir == +1) {
-           while (p.x < cols
-                  && wordness[ldata[p.x + 1] & CHAR_MASK] ==
-                  wvalue) p.x++;
+           while (1) {
+               if (p.x < cols-1) {
+                   if (wordtype(ldata[p.x + 1]) == wvalue)
+                       p.x++;
+                   else
+                       break;
+               } else {
+                   if (ldata[cols] & LATTR_WRAPPED) {
+                       unsigned long *ldata2;
+                       ldata2 = lineptr(p.y+1);
+                       if (wordtype(ldata2[0]) == wvalue) {
+                           p.x = 0;
+                           p.y++;
+                           ldata = ldata2;
+                       } else
+                           break;
+                   } else
+                       break;
+               }
+           }
        } else {
-           while (p.x > 0
-                  && wordness[ldata[p.x - 1] & CHAR_MASK] ==
-                  wvalue) p.x--;
+           while (1) {
+               if (p.x > 0) {
+                   if (wordtype(ldata[p.x - 1]) == wvalue)
+                       p.x--;
+                   else
+                       break;
+               } else {
+                   unsigned long *ldata2;
+                   if (p.y <= topy)
+                       break;
+                   ldata2 = lineptr(p.y-1);
+                   if ((ldata2[cols] & LATTR_WRAPPED) &&
+                       wordtype(ldata2[cols-1]) == wvalue) {
+                       p.x = cols-1;
+                       p.y--;
+                       ldata = ldata2;
+                   } else
+                       break;
+               }
+           }
        }
        break;
       case SM_LINE:
@@ -2382,22 +3435,82 @@ static pos sel_spread_half(pos p, int dir)
 
 static void sel_spread(void)
 {
-    selstart = sel_spread_half(selstart, -1);
-    decpos(selend);
-    selend = sel_spread_half(selend, +1);
-    incpos(selend);
+    if (seltype == LEXICOGRAPHIC) {
+       selstart = sel_spread_half(selstart, -1);
+       decpos(selend);
+       selend = sel_spread_half(selend, +1);
+       incpos(selend);
+    }
+}
+
+void term_do_paste(void)
+{
+    wchar_t *data;
+    int len;
+
+    get_clip(&data, &len);
+    if (data) {
+        wchar_t *p, *q;
+
+        if (paste_buffer)
+            sfree(paste_buffer);
+        paste_pos = paste_hold = paste_len = 0;
+        paste_buffer = smalloc(len * sizeof(wchar_t));
+
+        p = q = data;
+        while (p < data + len) {
+            while (p < data + len &&
+                   !(p <= data + len - sel_nl_sz &&
+                     !memcmp(p, sel_nl, sizeof(sel_nl))))
+                p++;
+
+            {
+                int i;
+                for (i = 0; i < p - q; i++) {
+                    paste_buffer[paste_len++] = q[i];
+                }
+            }
+
+            if (p <= data + len - sel_nl_sz &&
+                !memcmp(p, sel_nl, sizeof(sel_nl))) {
+                paste_buffer[paste_len++] = '\r';
+                p += sel_nl_sz;
+            }
+            q = p;
+        }
+
+        /* Assume a small paste will be OK in one go. */
+        if (paste_len < 256) {
+            luni_send(paste_buffer, paste_len, 0);
+            if (paste_buffer)
+                sfree(paste_buffer);
+            paste_buffer = 0;
+            paste_pos = paste_hold = paste_len = 0;
+        }
+    }
+    get_clip(NULL, NULL);
 }
 
 void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
-               int shift, int ctrl)
+               int shift, int ctrl, int alt)
 {
     pos selpoint;
     unsigned long *ldata;
+    int raw_mouse = (xterm_mouse &&
+                    !cfg.no_mouse_rep &&
+                    !(cfg.mouse_override && shift));
+    int default_seltype;
 
-    if (y < 0)
+    if (y < 0) {
        y = 0;
-    if (y >= rows)
+       if (a == MA_DRAG && !raw_mouse)
+           term_scroll(0, -1);
+    }
+    if (y >= rows) {
        y = rows - 1;
+       if (a == MA_DRAG && !raw_mouse)
+           term_scroll(0, +1);
+    }
     if (x < 0) {
        if (y > 0) {
            x = cols - 1;
@@ -2414,7 +3527,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
     if ((ldata[cols] & LATTR_MODE) != LATTR_NORM)
        selpoint.x /= 2;
 
-    if (xterm_mouse) {
+    if (raw_mouse) {
        int encstate = 0, r, c;
        char abuf[16];
        static int is_down = 0;
@@ -2435,6 +3548,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
          case MBT_WHEEL_DOWN:
            encstate = 0x61;
            break;
+         default: break;              /* placate gcc warning about enum use */
        }
        switch (a) {
          case MA_DRAG:
@@ -2451,6 +3565,7 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
                return;
            is_down = b;
            break;
+         default: break;              /* placate gcc warning about enum use */
        }
        if (shift)
            encstate += 0x04;
@@ -2460,15 +3575,29 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
        c = x + 33;
 
        sprintf(abuf, "\033[M%c%c%c", encstate, c, r);
-       ldisc_send(abuf, 6);
+       ldisc_send(abuf, 6, 0);
        return;
     }
 
     b = translate_button(b);
 
+    /*
+     * Set the selection type (rectangular or normal) at the start
+     * of a selection attempt, from the state of Alt.
+     */
+    if (!alt ^ !cfg.rect_select)
+       default_seltype = RECTANGULAR;
+    else
+       default_seltype = LEXICOGRAPHIC;
+       
+    if (selstate == NO_SELECTION) {
+       seltype = default_seltype;
+    }
+
     if (b == MBT_SELECT && a == MA_CLICK) {
        deselect();
        selstate = ABOUT_TO;
+       seltype = default_seltype;
        selanchor = selpoint;
        selmode = SM_CHAR;
     } else if (b == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
@@ -2484,26 +3613,64 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
        if (selstate == ABOUT_TO && poseq(selanchor, selpoint))
            return;
        if (b == MBT_EXTEND && a != MA_DRAG && selstate == SELECTED) {
-           if (posdiff(selpoint, selstart) <
-               posdiff(selend, selstart) / 2) {
-               selanchor = selend;
-               decpos(selanchor);
+           if (seltype == LEXICOGRAPHIC) {
+               /*
+                * For normal selection, we extend by moving
+                * whichever end of the current selection is closer
+                * to the mouse.
+                */
+               if (posdiff(selpoint, selstart) <
+                   posdiff(selend, selstart) / 2) {
+                   selanchor = selend;
+                   decpos(selanchor);
+               } else {
+                   selanchor = selstart;
+               }
            } else {
-               selanchor = selstart;
+               /*
+                * For rectangular selection, we have a choice of
+                * _four_ places to put selanchor and selpoint: the
+                * four corners of the selection.
+                */
+               if (2*selpoint.x < selstart.x + selend.x)
+                   selanchor.x = selend.x-1;
+               else
+                   selanchor.x = selstart.x;
+
+               if (2*selpoint.y < selstart.y + selend.y)
+                   selanchor.y = selend.y;
+               else
+                   selanchor.y = selstart.y;
            }
            selstate = DRAGGING;
        }
        if (selstate != ABOUT_TO && selstate != DRAGGING)
            selanchor = selpoint;
        selstate = DRAGGING;
-       if (poslt(selpoint, selanchor)) {
-           selstart = selpoint;
-           selend = selanchor;
-           incpos(selend);
+       if (seltype == LEXICOGRAPHIC) {
+           /*
+            * For normal selection, we set (selstart,selend) to
+            * (selpoint,selanchor) in some order.
+            */
+           if (poslt(selpoint, selanchor)) {
+               selstart = selpoint;
+               selend = selanchor;
+               incpos(selend);
+           } else {
+               selstart = selanchor;
+               selend = selpoint;
+               incpos(selend);
+           }
        } else {
-           selstart = selanchor;
-           selend = selpoint;
-           incpos(selend);
+           /*
+            * For rectangular selection, we may need to
+            * interchange x and y coordinates (if the user has
+            * dragged in the -x and +y directions, or vice versa).
+            */
+           selstart.x = min(selanchor.x, selpoint.x);
+           selend.x = 1+max(selanchor.x, selpoint.x);
+           selstart.y = min(selanchor.y, selpoint.y);
+           selend.y =   max(selanchor.y, selpoint.y);
        }
        sel_spread();
     } else if ((b == MBT_SELECT || b == MBT_EXTEND) && a == MA_RELEASE) {
@@ -2512,58 +3679,13 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
             * We've completed a selection. We now transfer the
             * data to the clipboard.
             */
-           clipme(selstart, selend, selspace);
+           clipme(selstart, selend, (seltype == RECTANGULAR));
            selstate = SELECTED;
        } else
            selstate = NO_SELECTION;
     } else if (b == MBT_PASTE
               && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
-       char *data;
-       int len;
-
-       get_clip((void **) &data, &len);
-       if (data) {
-           char *p, *q;
-
-           if (paste_buffer)
-               sfree(paste_buffer);
-           paste_pos = paste_hold = paste_len = 0;
-           paste_buffer = smalloc(len);
-
-           p = q = data;
-           while (p < data + len) {
-               while (p < data + len &&
-                      !(p <= data + len - sizeof(sel_nl) &&
-                        !memcmp(p, sel_nl, sizeof(sel_nl))))
-                   p++;
-
-               {
-                   int i;
-                   unsigned char c;
-                   for (i = 0; i < p - q; i++) {
-                       c = xlat_kbd2tty(q[i]);
-                       paste_buffer[paste_len++] = c;
-                   }
-               }
-
-               if (p <= data + len - sizeof(sel_nl) &&
-                   !memcmp(p, sel_nl, sizeof(sel_nl))) {
-                   paste_buffer[paste_len++] = '\r';
-                   p += sizeof(sel_nl);
-               }
-               q = p;
-           }
-
-           /* Assume a small paste will be OK in one go. */
-           if (paste_len < 256) {
-               ldisc_send(paste_buffer, paste_len);
-               if (paste_buffer)
-                   sfree(paste_buffer);
-               paste_buffer = 0;
-               paste_pos = paste_hold = paste_len = 0;
-           }
-       }
-       get_clip(NULL, NULL);
+        term_do_paste();
     }
 
     term_update();
@@ -2588,7 +3710,7 @@ void term_paste()
 
     /* Don't wait forever to paste */
     if (paste_hold) {
-       now = GetTickCount();
+       now = GETTICKCOUNT();
        paste_diff = now - last_paste;
        if (paste_diff >= 0 && paste_diff < 450)
            return;
@@ -2601,7 +3723,7 @@ void term_paste()
            if (paste_buffer[paste_pos + n++] == '\r')
                break;
        }
-       ldisc_send(paste_buffer + paste_pos, n);
+       luni_send(paste_buffer + paste_pos, n, 0);
        paste_pos += n;
 
        if (paste_pos < paste_len) {
@@ -2638,81 +3760,30 @@ int term_ldisc(int option)
 /*
  * from_backend(), to get data from the backend for the terminal.
  */
-void from_backend(int is_stderr, char *data, int len)
-{
-    while (len--) {
-       if (inbuf_head >= INBUF_SIZE)
-           term_out();
-       inbuf[inbuf_head++] = *data++;
-    }
-}
-
-/*
- * Log session traffic.
- */
-void logtraffic(unsigned char c, int logmode)
+int from_backend(int is_stderr, char *data, int len)
 {
-    if (cfg.logtype > 0) {
-       if (cfg.logtype == logmode) {
-           /* deferred open file from pgm start? */
-           if (!lgfp)
-               logfopen();
-           if (lgfp)
-               fputc(c, lgfp);
-       }
-    }
-}
+    assert(len > 0);
 
-/* open log file append/overwrite mode */
-void logfopen(void)
-{
-    char buf[256];
-    time_t t;
-    struct tm *tm;
-    char writemod[4];
+    bufchain_add(&inbuf, data, len);
 
-    if (!cfg.logtype)
-       return;
-    sprintf(writemod, "wb");          /* default to rewrite */
-    lgfp = fopen(cfg.logfilename, "r");        /* file already present? */
-    if (lgfp) {
-       int i;
-       fclose(lgfp);
-       i = askappend(cfg.logfilename);
-       if (i == 1)
-           writemod[0] = 'a';         /* set append mode */
-       else if (i == 0) {             /* cancelled */
-           lgfp = NULL;
-           cfg.logtype = 0;           /* disable logging */
-           return;
-       }
-    }
-
-    lgfp = fopen(cfg.logfilename, writemod);
-    if (lgfp) {                               /* enter into event log */
-       sprintf(buf, "%s session log (%s mode) to file : ",
-               (writemod[0] == 'a') ? "Appending" : "Writing new",
-               (cfg.logtype == LGTYP_ASCII ? "ASCII" :
-                cfg.logtype == LGTYP_DEBUG ? "raw" : "<ukwn>"));
-       /* Make sure we do not exceed the output buffer size */
-       strncat(buf, cfg.logfilename, 128);
-       buf[strlen(buf)] = '\0';
-       logevent(buf);
-
-       /* --- write header line iinto log file */
-       fputs("=~=~=~=~=~=~=~=~=~=~=~= PuTTY log ", lgfp);
-       time(&t);
-       tm = localtime(&t);
-       strftime(buf, 24, "%Y.%m.%d %H:%M:%S", tm);
-       fputs(buf, lgfp);
-       fputs(" =~=~=~=~=~=~=~=~=~=~=~=\r\n", lgfp);
-    }
-}
-
-void logfclose(void)
-{
-    if (lgfp) {
-       fclose(lgfp);
-       lgfp = NULL;
-    }
+    /*
+     * 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 SSH2 - 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 SSH1 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;
 }