Robert de Bath's patch: be a little more careful of main and
[u/mdw/putty] / terminal.c
index b41eacd..b0cc354 100644 (file)
@@ -46,6 +46,8 @@ static int alt_which;
 static int esc_args[ARGS_MAX];
 static int esc_nargs;
 static int esc_query;
+#define ANSI(x,y)      ((x)+((y)<<8))
+#define ANSI_QUE(x)    ANSI(x,TRUE)
 
 #define OSC_STR_MAX 2048
 static int osc_strlen;
@@ -60,9 +62,18 @@ static int nl_count;
 static int scroll_heuristic;
 
 static enum {
-    TOPLEVEL, IGNORE_NEXT,
-    SEEN_ESC, SEEN_CSI, SET_GL, SET_GR,
-    SEEN_OSC, SEEN_OSC_P, SEEN_OSC_W, OSC_STRING, OSC_MAYBE_ST,
+    TOPLEVEL,
+    SEEN_ESC,
+    SEEN_CSI,
+    SEEN_OSC,
+    SEEN_OSC_W,
+
+    DO_CTRLS,
+
+    IGNORE_NEXT,
+    SET_GL, SET_GR,
+    SEEN_OSC_P,
+    OSC_STRING, OSC_MAYBE_ST,
     SEEN_ESCHASH
 } termstate;
 
@@ -141,6 +152,11 @@ void term_update(void) {
     Context ctx;
     ctx = get_ctx();
     if (ctx) {
+        if ( (seen_key_event && (unscroll_event & US_KEY)) ||
+            (seen_disp_event && (unscroll_event & US_DISP)) ) {
+           disptop = scrtop;
+           seen_disp_event = seen_key_event = 0;
+       }
        do_paint (ctx, TRUE);
        free_ctx (ctx);
        nl_count = 0;
@@ -188,9 +204,14 @@ void term_size(int newrows, int newcols, int newsavelines) {
     unsigned long *newtext, *newdisp, *newwant, *newalt;
     int i, j, crows, ccols;
 
+    int save_alt_which = alt_which;
+
     if (newrows == rows && newcols == cols && newsavelines == savelines)
        return;                        /* nothing to do */
 
+    deselect();
+    swap_screen(0);
+
     alt_t = marg_t = 0;
     alt_b = marg_b = newrows - 1;
 
@@ -264,7 +285,8 @@ void term_size(int newrows, int newcols, int newsavelines) {
     savelines = newsavelines;
     fix_cpos;
 
-    deselect();
+    swap_screen(save_alt_which);
+
     update_sbar();
     term_update();
 }
@@ -345,10 +367,10 @@ static void scroll (int topline, int botline, int lines, int sb) {
     size = (lines < 0 ? -lines : lines) * (cols+1);
     scroll_size = (botline - topline + 1) * (cols+1) - size;
 
-    if (lines > 0 && topline == 0 && botline == (rows-1) && sb) {
+    if (lines > 0 && topline == 0 && alt_which == 0 && sb) {
        /*
-        * Since we're going to scroll the whole screen upwards,
-        * let's also affect the scrollback buffer.
+        * Since we're going to scroll the top line and we're on the 
+        * scrolling screen let's also affect the scrollback buffer.
         */
        sbtop -= lines * (cols+1);
        if (sbtop < text)
@@ -511,7 +533,7 @@ static void toggle_mode (int mode, int query, int state) {
        break;
       case 5:                         /* reverse video */
        rvideo = state;
-       disptop = scrtop;
+       seen_disp_event = TRUE;
        break;
       case 6:                         /* DEC origin mode */
        dec_om = state;
@@ -562,27 +584,25 @@ static void do_osc(void) {
  */
 void term_out(void) {
     int c;
-    int must_update = FALSE;
 
     while ( (c = inbuf_getc()) != -1) {
-#ifdef LOG
-       {
+        /*
+         * Optionally log the session traffic to a file. Useful for
+         * debugging and possibly also useful for actual logging.
+         */
+       if (logfile) {
            static FILE *fp = NULL;
-           if (!fp) fp = fopen("putty.log", "wb");
+           if (!fp) fp = fopen(logfile, "wb");
            if (fp) fputc (c, fp);
        }
-#endif
-       switch (termstate) {
-         case TOPLEVEL:
-           do_toplevel:
+       if( termstate < DO_CTRLS && (c&0x60) == 0 ) {
            switch (c) {
              case '\005':             /* terminal type query */
-               back->send ("\033[?1;2c", 7);
+               ldisc->send ("PuTTY\r", 6);
                break;
              case '\007':
                beep();
                disptop = scrtop;
-               must_update = TRUE;
                break;
              case '\b':
                if (curs_x == 0 && curs_y > 0)
@@ -592,8 +612,7 @@ void term_out(void) {
                else
                    curs_x--;
                fix_cpos;
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case '\016':
                cset = 1;
@@ -618,8 +637,7 @@ void term_out(void) {
                curs_x = 0;
                wrapnext = FALSE;
                fix_cpos;
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case '\013':
              case '\014':
@@ -628,11 +646,11 @@ void term_out(void) {
                    scroll (marg_t, marg_b, 1, TRUE);
                else if (curs_y < rows-1)
                    curs_y++;
-                if (cfg.lfhascr)
-                    curs_x = 0;
+               if (cfg.lfhascr)
+                   curs_x = 0;
                fix_cpos;
                wrapnext = FALSE;
-               disptop = scrtop;
+               seen_disp_event = 1;
                nl_count++;
                break;
              case '\t':
@@ -646,37 +664,38 @@ void term_out(void) {
                    fix_cpos;
                    check_selection (old_cpos, cpos);
                }
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
-             default:
-               if (c >= ' ' && c != 0234) {
-                   if (wrapnext) {
-                       cpos[1] = ATTR_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;
-                       nl_count++;
-                   }
-                   if (insert)
-                       insch (1);
-                   check_selection (cpos, cpos+1);
-                   *cpos++ = c | curr_attr | 
-                       (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
-                   curs_x++;
-                   if (curs_x == cols) {
-                       cpos--;
-                       curs_x--;
-                       wrapnext = wrap;
-                   }
-                   disptop = scrtop;
-               }
            }
+       }
+       else switch (termstate) {
+         case TOPLEVEL:
+         /* Only graphic characters get this far, ctrls are stripped above */
+           if (wrapnext) {
+               cpos[1] = ATTR_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;
+               nl_count++;
+           }
+           if (insert)
+               insch (1);
+           check_selection (cpos, cpos+1);
+           *cpos++ = xlat_tty2scr((unsigned char)c) | curr_attr |
+               (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
+           curs_x++;
+           if (curs_x == cols) {
+               cpos--;
+               curs_x--;
+               wrapnext = wrap;
+           }
+           seen_disp_event = 1;
            break;
+
          case IGNORE_NEXT:
            termstate = TOPLEVEL;
            break;
@@ -695,11 +714,6 @@ void term_out(void) {
          case SEEN_ESC:
            termstate = TOPLEVEL;
            switch (c) {
-             case '\005': case '\007': case '\b': case '\016': case '\017':
-             case '\033': case 0233: case 0234: case 0235: case '\r':
-             case '\013': case '\014': case '\n': case '\t':
-               termstate = TOPLEVEL;
-               goto do_toplevel;      /* hack... */
              case ' ':                /* some weird sequence? */
                termstate = IGNORE_NEXT;
                break;
@@ -724,8 +738,7 @@ void term_out(void) {
                break;
              case '8':                /* restore cursor */
                save_cursor (FALSE);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case '=':
                app_keypad_keys = TRUE;
@@ -740,7 +753,7 @@ void term_out(void) {
                    curs_y++;
                fix_cpos;
                wrapnext = FALSE;
-               disptop = scrtop;
+               seen_disp_event = TRUE;
                nl_count++;
                break;
              case 'E':                /* exactly equivalent to CR-LF */
@@ -753,7 +766,7 @@ void term_out(void) {
                fix_cpos;
                wrapnext = FALSE;
                nl_count++;
-               disptop = scrtop;
+               seen_disp_event = TRUE;
                break;
              case 'M':                /* reverse index - backwards LF */
                if (curs_y == marg_t)
@@ -762,17 +775,16 @@ void term_out(void) {
                    curs_y--;
                fix_cpos;
                wrapnext = FALSE;
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'Z':                /* terminal type query */
-               back->send ("\033[?6c", 5);
+               ldisc->send ("\033[?6c", 5);
                break;
              case 'c':                /* restore power-on settings */
                power_on();
                fix_cpos;
                disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case '#':                /* ESC # 8 fills screen with Es :-) */
                termstate = SEEN_ESCHASH;
@@ -784,14 +796,8 @@ void term_out(void) {
            break;
          case SEEN_CSI:
            termstate = TOPLEVEL;      /* default */
-           switch (c) {
-             case '\005': case '\007': case '\b': case '\016': case '\017':
-             case '\033': case 0233: case 0234: case 0235: case '\r':
-             case '\013': case '\014': case '\n': case '\t':
-               termstate = TOPLEVEL;
-               goto do_toplevel;      /* hack... */
-             case '0': case '1': case '2': case '3': case '4':
-             case '5': case '6': case '7': case '8': case '9':
+           if( isdigit(c) )
+           {
                if (esc_nargs <= ARGS_MAX) {
                    if (esc_args[esc_nargs-1] == ARG_DEFAULT)
                        esc_args[esc_nargs-1] = 0;
@@ -799,56 +805,53 @@ void term_out(void) {
                        10 * esc_args[esc_nargs-1] + c - '0';
                }
                termstate = SEEN_CSI;
-               break;
-             case ';':
+           }
+           else if( c == ';' )
+           {
                if (++esc_nargs <= ARGS_MAX)
                    esc_args[esc_nargs-1] = ARG_DEFAULT;
                termstate = SEEN_CSI;
-               break;
-             case '?':
-               esc_query = TRUE;
+           }
+           else if( c < '@' )
+           {
+               if( esc_query )     esc_query = -1;
+               else if( c == '?' ) esc_query = TRUE;
+               else                esc_query = c;
                termstate = SEEN_CSI;
-               break;
+           }
+           else switch (ANSI(c,esc_query)) {
              case 'A':                /* move up N lines */
                move (curs_x, curs_y - def(esc_args[0], 1), 1);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'B': case 'e':      /* move down N lines */
                move (curs_x, curs_y + def(esc_args[0], 1), 1);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'C': case 'a':      /* move right N cols */
                move (curs_x + def(esc_args[0], 1), curs_y, 1);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'D':                /* move left N cols */
                move (curs_x - def(esc_args[0], 1), curs_y, 1);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'E':                /* move down N lines and CR */
                move (0, curs_y + def(esc_args[0], 1), 1);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'F':                /* move up N lines and CR */
                move (0, curs_y - def(esc_args[0], 1), 1);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'G': case '`':      /* set horizontal posn */
                move (def(esc_args[0], 1) - 1, curs_y, 0);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'd':                /* set vertical posn */
                move (curs_x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
                      (dec_om ? 2 : 0));
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'H': case 'f':      /* set horz and vert posns at once */
                if (esc_nargs < 2)
@@ -856,8 +859,7 @@ void term_out(void) {
                move (def(esc_args[1], 1) - 1,
                      (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
                      (dec_om ? 2 : 0));
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'J':                /* erase screen or parts of it */
                {
@@ -867,7 +869,7 @@ void term_out(void) {
                    erase_lots(FALSE, !!(i & 2), !!(i & 1));
                }
                disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'K':                /* erase line or parts of it */
                {
@@ -876,45 +878,42 @@ void term_out(void) {
                        i = 0;
                    erase_lots(TRUE, !!(i & 2), !!(i & 1));
                }
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'L':                /* insert lines */
                if (curs_y <= marg_b)
                    scroll (curs_y, marg_b, -def(esc_args[0], 1), FALSE);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'M':                /* delete lines */
                if (curs_y <= marg_b)
-                   scroll (curs_y, marg_b, def(esc_args[0], 1), FALSE);
-               disptop = scrtop;
-               must_update = TRUE;
+                   scroll (curs_y, marg_b, def(esc_args[0], 1), TRUE);
+               seen_disp_event = TRUE;
                break;
              case '@':                /* insert chars */
                insch (def(esc_args[0], 1));
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'P':                /* delete chars */
                insch (-def(esc_args[0], 1));
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 'c':                /* terminal type query */
-               back->send ("\033[?6c", 5);
+               ldisc->send ("\033[?6c", 5);
                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);
-                   back->send (buf, strlen(buf));
+                   ldisc->send (buf, strlen(buf));
                }
                break;
              case 'h':                /* toggle a mode to high */
+             case ANSI_QUE('h'):
                toggle_mode (esc_args[0], esc_query, TRUE);
                break;
              case 'l':                /* toggle a mode to low */
+             case ANSI_QUE('l'):
                toggle_mode (esc_args[0], esc_query, FALSE);
                break;
              case 'g':                /* clear tabs */
@@ -934,7 +933,7 @@ void term_out(void) {
                    top = def(esc_args[0], 1) - 1;
                    if (top < 0)
                        top = 0;
-                   bot = (esc_nargs == 1 ? rows :
+                   bot = (esc_nargs <= 1 || esc_args[1] == 0 ? rows :
                           def(esc_args[1], rows)) - 1;
                    if (bot >= rows)
                        bot = rows-1;
@@ -950,8 +949,7 @@ void term_out(void) {
                         */
                        curs_y = 0;
                        fix_cpos;
-                       disptop = scrtop;
-                       must_update = TRUE;
+                       seen_disp_event = TRUE;
                    }
                }
                break;
@@ -1004,8 +1002,7 @@ void term_out(void) {
                break;
              case 'u':                /* restore cursor */
                save_cursor (FALSE);
-               disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                break;
              case 't':                /* set page size - ie window height */
                request_resize (cols, def(esc_args[0], 24));
@@ -1020,8 +1017,7 @@ void term_out(void) {
                    check_selection (cpos, cpos+n);
                    while (n--)
                        *p++ = ERASE_CHAR;
-                   disptop = scrtop;
-                   must_update = TRUE;
+                   seen_disp_event = TRUE;
                }
                break;
              case 'x':                /* report terminal characteristics */
@@ -1031,7 +1027,7 @@ void term_out(void) {
                    if (i == 0 || i == 1) {
                        strcpy (buf, "\033[2;1;1;112;112;1;0x");
                        buf[2] += i;
-                       back->send (buf, 20);
+                       ldisc->send (buf, 20);
                    }
                }
                break;
@@ -1050,16 +1046,12 @@ void term_out(void) {
                cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
                break;
            }
-           termstate = TOPLEVEL;
+           if( c != '%' )
+               termstate = TOPLEVEL;
            break;
          case SEEN_OSC:
            osc_w = FALSE;
            switch (c) {
-             case '\005': case '\007': case '\b': case '\016': case '\017':
-             case '\033': case 0233: case 0234: case 0235: case '\r':
-             case '\013': case '\014': case '\n': case '\t':
-               termstate = TOPLEVEL;
-               goto do_toplevel;      /* hack... */
              case 'P':                /* Linux palette sequence */
                termstate = SEEN_OSC_P;
                osc_strlen = 0;
@@ -1093,7 +1085,19 @@ void term_out(void) {
            }
            break;
          case OSC_STRING:
-           if (c == 0234 || c == '\007') {
+           /*
+            * This OSC stuff is EVIL. It takes just one character to get into
+            * sysline mode and it's not initially obvious how to get out.
+            * So I've added CR and LF as string aborts.
+            * This shouldn't effect compatibility as I believe embedded 
+            * control characters are supposed to be interpreted (maybe?) 
+            * and they don't display anything useful anyway.
+            *
+            * -- RDB
+            */
+           if (c == '\n' || c == '\r') {
+               termstate = TOPLEVEL;
+           } else if (c == 0234 || c == '\007' ) {
                /*
                 * These characters terminate the string; ST and BEL
                 * terminate the sequence and trigger instant
@@ -1133,11 +1137,6 @@ void term_out(void) {
            break;
          case SEEN_OSC_W:
            switch (c) {
-             case '\005': case '\007': case '\b': case '\016': case '\017':
-             case '\033': case 0233: case 0234: case 0235: case '\r':
-             case '\013': case '\014': case '\n': case '\t':
-               termstate = TOPLEVEL;
-               goto do_toplevel;      /* hack... */
              case '0': case '1': case '2': case '3': case '4':
              case '5': case '6': case '7': case '8': case '9':
                esc_args[0] = 10 * esc_args[0] + c - '0';
@@ -1154,7 +1153,7 @@ void term_out(void) {
                while (n--)
                    *p++ = ATTR_DEFAULT | 'E';
                disptop = scrtop;
-               must_update = TRUE;
+               seen_disp_event = TRUE;
                check_selection (scrtop, scrtop + rows * (cols+1));
            }
            termstate = TOPLEVEL;
@@ -1163,7 +1162,7 @@ void term_out(void) {
        check_selection (cpos, cpos+1);
     }
        
-    if (must_update || nl_count > MAXNL)
+    if (nl_count > MAXNL)
        term_update();
 }
 
@@ -1345,7 +1344,13 @@ void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
     
     if (y<0) y = 0;
     if (y>=rows) y = rows-1;
-    if (x<0) x = 0;
+    if (x<0) {
+        if (y > 0) {
+            x = cols-1;
+            y--;
+        } else
+            x = 0;
+    }
     if (x>=cols) x = cols-1;
 
     selpoint = disptop + y * (cols+1) + x;
@@ -1430,10 +1435,20 @@ void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
                       !(p <= data+len-sizeof(sel_nl) &&
                         !memcmp(p, sel_nl, sizeof(sel_nl))))
                    p++;
-               back->send (q, p-q);
+
+               {
+                   int i;
+                   unsigned char c;
+                   for(i=0;i<p-q;i++)
+                   {
+                       c=xlat_kbd2tty(q[i]);
+                       ldisc->send(&c,1);
+                   }
+               }
+
                if (p <= data+len-sizeof(sel_nl) &&
                    !memcmp(p, sel_nl, sizeof(sel_nl))) {
-                   back->send ("\r", 1);
+                   ldisc->send ("\r", 1);
                    p += sizeof(sel_nl);
                }
                q = p;