Rethink the whole line discipline architecture. Instead of having
[u/mdw/putty] / window.c
index ea898e9..cef6825 100644 (file)
--- a/window.c
+++ b/window.c
@@ -40,6 +40,7 @@
 #define IDM_TEL_EOF   0x0130
 #define IDM_ABOUT     0x0140
 #define IDM_SAVEDSESS 0x0150
+#define IDM_COPYALL   0x0160
 
 #define IDM_SAVED_MIN 0x1000
 #define IDM_SAVED_MAX 0x2000
@@ -100,13 +101,10 @@ static Mouse_Button lastbtn;
 
 static char *window_name, *icon_name;
 
-static Ldisc *real_ldisc;
-
 static int compose_state = 0;
 
-void begin_session(void) {
-    ldisc = real_ldisc;
-}
+/* Dummy routine, only required in plink. */
+void ldisc_update(int echo, int edit) {}
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     static char appname[] = "PuTTY";
@@ -148,6 +146,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
 
        default_protocol = DEFAULT_PROTOCOL;
        default_port = DEFAULT_PORT;
+       cfg.logtype = LGTYP_NONE;
 
        do_defaults(NULL, &cfg);
 
@@ -168,11 +167,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                tolower(p[2]) == 'h') {
                default_protocol = cfg.protocol = PROT_SSH;
                default_port = cfg.port = 22;
-           } else if (q == p + 3 &&
-               tolower(p[0]) == 'l' &&
-               tolower(p[1]) == 'o' &&
-               tolower(p[2]) == 'g') {
-                logfile = "putty.log";
            } else if (q == p + 7 &&
                tolower(p[0]) == 'c' &&
                tolower(p[1]) == 'l' &&
@@ -313,10 +307,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         }
     }
 
-    real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
-    /* To start with, we use the simple line discipline, so we can
-     * type passwords etc without fear of them being echoed... */
-    ldisc = &ldisc_simple;
+    /* Check for invalid Port number (i.e. zero) */
+    if (cfg.port == 0) {
+        MessageBox(NULL, "Invalid Port Number",
+                  "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
+        WSACleanup();
+        return 1;
+    }
 
     if (!prev) {
        wndclass.style         = 0;
@@ -412,10 +409,12 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     {
         char *bits;
         int size = (font_width+15)/16 * 2 * font_height; 
-        bits = calloc(size, 1);
+        bits = smalloc(size);
+        memset(bits, 0, size);
         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
-        free(bits);
+        sfree(bits);
     }
+    CreateCaret(hwnd, caretbm, font_width, font_height);
 
     /*
      * Initialise the scroll bar.
@@ -513,6 +512,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
        AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
        AppendMenu (m, MF_SEPARATOR, 0, 0);
+       AppendMenu (m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
        AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
        AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
        AppendMenu (m, MF_SEPARATOR, 0, 0);
@@ -525,6 +525,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     ShowWindow (hwnd, show);
 
     /*
+     * Open the initial log file if there is one.
+     */
+    logfopen();
+
+    /*
      * Set the palette up.
      */
     pal = NULL;
@@ -551,7 +556,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
            }
            if(!timer_id)
                timer_id = SetTimer(hwnd, 1, 20, NULL);
-           DispatchMessage (&msg);
+            if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
+                DispatchMessage (&msg);
 
            /* Make sure we blink everything that needs it. */
            term_blink(0);
@@ -592,7 +598,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
            if (!has_focus)
               timer_id = SetTimer(hwnd, 1, 59500, NULL);
            else
-              timer_id = SetTimer(hwnd, 1, 250, NULL);
+              timer_id = SetTimer(hwnd, 1, 100, NULL);
            long_timer = 1;
        
            /* There's no point rescanning everything in the message queue
@@ -1041,6 +1047,17 @@ static void click (Mouse_Button b, int x, int y) {
     lasttime = thistime;
 }
 
+static void show_mouseptr(int show) {
+    static int cursor_visible = 1;
+    if (!cfg.hide_mouseptr)            /* override if this feature disabled */
+        show = 1;
+    if (cursor_visible && !show)
+        ShowCursor(FALSE);
+    else if (!cursor_visible && show)
+        ShowCursor(TRUE);
+    cursor_visible = show;
+}
+
 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                                  WPARAM wParam, LPARAM lParam) {
     HDC hdc;
@@ -1048,6 +1065,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
     static int ignore_clip = FALSE;
     static int just_reconfigged = FALSE;
     static int resizing = FALSE;
+    static int need_backend_resize = FALSE;
 
     switch (message) {
       case WM_TIMER:
@@ -1063,7 +1081,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
         {
            time_t now;
            time(&now);
-           if (now-last_movement > cfg.ping_interval * 60 - 10)
+           if (now-last_movement > cfg.ping_interval)
            {
               back->special(TS_PING);
               last_movement = now;
@@ -1073,6 +1091,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
       case WM_CREATE:
        break;
       case WM_CLOSE:
+        show_mouseptr(1);
        if (!cfg.warn_on_close || session_closed ||
            MessageBox(hwnd, "Are you sure you want to close this session?",
                       "PuTTY Exit Confirmation",
@@ -1080,6 +1099,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            DestroyWindow(hwnd);
        return 0;
       case WM_DESTROY:
+        show_mouseptr(1);
        PostQuitMessage (0);
        return 0;
       case WM_SYSCOMMAND:
@@ -1128,7 +1148,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                    cl = c;
                } else if (wParam == IDM_SAVEDSESS) {
                    char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
-                   cl = malloc(16 + strlen(session)); /* 8, but play safe */
+                   cl = smalloc(16 + strlen(session)); /* 8, but play safe */
                    if (!cl)
                        cl = NULL;     /* not a very important failure mode */
                    else {
@@ -1152,14 +1172,34 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                if (filemap)
                    CloseHandle(filemap);
                if (freecl)
-                   free(cl);
+                   sfree(cl);
            }
            break;
           case IDM_RECONF:
             {
                 int prev_alwaysontop = cfg.alwaysontop;
+               char oldlogfile[FILENAME_MAX];
+               int oldlogtype;
+               int need_setwpos = FALSE;
+               int old_fwidth, old_fheight;
+
+               strcpy(oldlogfile, cfg.logfilename);
+               oldlogtype = cfg.logtype;
+               cfg.width = cols;
+               cfg.height = rows;
+               old_fwidth = font_width;
+               old_fheight = font_height;
+                GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
+
                 if (!do_reconfig(hwnd))
                     break;
+
+               if (strcmp(oldlogfile, cfg.logfilename) ||
+                   oldlogtype != cfg.logtype) {
+                   logfclose();       /* reset logging */
+                   logfopen();
+               }
+
                 just_reconfigged = TRUE;
                 {
                     int i;
@@ -1172,11 +1212,10 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                 init_fonts(0);
                 sfree(logpal);
                 /*
-                 * Telnet will change local echo -> remote if the
-                 * remote asks.
+                 * Flush the line discipline's edit buffer in the
+                 * case where local editing has just been disabled.
                  */
-                if (cfg.protocol != PROT_TELNET)
-                    ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
+                ldisc_send(NULL, 0);
                 if (pal)
                     DeleteObject(pal);
                 logpal = NULL;
@@ -1220,6 +1259,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
 
                         SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
+
                         SetWindowPos(hwnd, NULL, 0,0,0,0,
                                      SWP_NOACTIVATE|SWP_NOCOPYBITS|
                                      SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
@@ -1232,19 +1272,32 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                     }
                 }
 
+               if (cfg.height != rows ||
+                   cfg.width != cols ||
+                   old_fwidth != font_width ||
+                   old_fheight != font_height ||
+                   cfg.savelines != savelines)
+                   need_setwpos = TRUE;
                 term_size(cfg.height, cfg.width, cfg.savelines);
                 InvalidateRect(hwnd, NULL, TRUE);
-                SetWindowPos (hwnd, NULL, 0, 0,
-                              extra_width + font_width * cfg.width,
-                              extra_height + font_height * cfg.height,
-                              SWP_NOACTIVATE | SWP_NOCOPYBITS |
-                              SWP_NOMOVE | SWP_NOZORDER);
+                if (need_setwpos) {
+                   force_normal(hwnd);
+                   SetWindowPos (hwnd, NULL, 0, 0,
+                                 extra_width + font_width * cfg.width,
+                                 extra_height + font_height * cfg.height,
+                                 SWP_NOACTIVATE | SWP_NOCOPYBITS |
+                                 SWP_NOMOVE | SWP_NOZORDER);
+               }
+                set_title(cfg.wintitle);
                 if (IsIconic(hwnd)) {
                     SetWindowText (hwnd,
                                    cfg.win_name_always ? window_name : icon_name);
                 }
             }
             break;
+         case IDM_COPYALL:
+           term_copyall();
+           break;
           case IDM_CLRSB:
             term_clrsb();
             break;
@@ -1281,40 +1334,47 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
 
       case WM_LBUTTONDOWN:
+        show_mouseptr(1);
        click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
               TO_CHR_Y(Y_POS(lParam)));
         SetCapture(hwnd);
        return 0;
       case WM_LBUTTONUP:
+        show_mouseptr(1);
        term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
                    TO_CHR_Y(Y_POS(lParam)));
         ReleaseCapture();
        return 0;
       case WM_MBUTTONDOWN:
+        show_mouseptr(1);
         SetCapture(hwnd);
        click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
               TO_CHR_X(X_POS(lParam)),
               TO_CHR_Y(Y_POS(lParam)));
        return 0;
       case WM_MBUTTONUP:
+        show_mouseptr(1);
        term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
                    MA_RELEASE, TO_CHR_X(X_POS(lParam)),
                    TO_CHR_Y(Y_POS(lParam)));
         ReleaseCapture();
        return 0;
       case WM_RBUTTONDOWN:
+        show_mouseptr(1);
         SetCapture(hwnd);
        click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
               TO_CHR_X(X_POS(lParam)),
               TO_CHR_Y(Y_POS(lParam)));
        return 0;
       case WM_RBUTTONUP:
+        show_mouseptr(1);
        term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
                    MA_RELEASE, TO_CHR_X(X_POS(lParam)),
                    TO_CHR_Y(Y_POS(lParam)));
         ReleaseCapture();
        return 0;
       case WM_MOUSEMOVE:
+        show_mouseptr(1);
        /*
         * Add the mouse position and message time to the random
         * number noise.
@@ -1373,13 +1433,14 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        return 0;
       case WM_SETFOCUS:
        has_focus = TRUE;
-        CreateCaret(hwnd, caretbm, 0, 0);
+        CreateCaret(hwnd, caretbm, font_width, font_height);
         ShowCaret(hwnd);
         compose_state = 0;
        term_out();
        term_update();
        break;
       case WM_KILLFOCUS:
+        show_mouseptr(1);
        has_focus = FALSE;
         DestroyCaret();
        term_out();
@@ -1391,11 +1452,13 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
       case WM_ENTERSIZEMOVE:
         EnableSizeTip(1);
         resizing = TRUE;
+       need_backend_resize = FALSE;
         break;
       case WM_EXITSIZEMOVE:
         EnableSizeTip(0);
         resizing = FALSE;
-        back->size();
+       if (need_backend_resize)
+           back->size();
         break;
       case WM_SIZING:
        {
@@ -1471,6 +1534,8 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                  */
                 if (!resizing)
                     back->size();
+               else
+                   need_backend_resize = TRUE;
                just_reconfigged = FALSE;
            }
        }
@@ -1541,7 +1606,10 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                len = TranslateKey (message, wParam, lParam, buf);
                if (len == -1)
                    return DefWindowProc (hwnd, message, wParam, lParam);
-               ldisc->send (buf, len);
+               ldisc_send (buf, len);
+
+                if (len > 0)
+                    show_mouseptr(0);
            }
        }
        return 0;
@@ -1551,7 +1619,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 
            buf[1] = wParam;
            buf[0] = wParam >> 8;
-           ldisc->send (buf, 2);
+           ldisc_send (buf, 2);
        }
       case WM_CHAR:
       case WM_SYSCHAR:
@@ -1563,7 +1631,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
         */
        {
            char c = xlat_kbd2tty((unsigned char)wParam);
-           ldisc->send (&c, 1);
+           ldisc_send (&c, 1);
        }
        return 0;
     }
@@ -1611,7 +1679,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
     x *= fnt_width;
     y *= font_height;
 
-    if (attr & ATTR_ACTCURS) {
+    if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
        attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
        attr ^= ATTR_CUR_XOR;
     }
@@ -1772,7 +1840,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
         oldpen = SelectObject (hdc, oldpen);
         DeleteObject (oldpen);
     }
-    if (attr & ATTR_PASCURS) {
+    if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
        POINT pts[5];
         HPEN oldpen;
        pts[0].x = pts[1].x = pts[4].x = x;
@@ -1784,6 +1852,34 @@ void do_text (Context ctx, int x, int y, char *text, int len,
         oldpen = SelectObject (hdc, oldpen);
         DeleteObject (oldpen);
     }
+    if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
+        int startx, starty, dx, dy, length, i;
+       if (cfg.cursor_type == 1) {
+            startx = x; starty = y+descent;
+            dx = 1; dy = 0; length = fnt_width;
+        } else {
+           int xadjust = 0;
+           if (attr & ATTR_RIGHTCURS)
+               xadjust = fnt_width-1;
+            startx = x+xadjust; starty = y;
+            dx = 0; dy = 1; length = font_height;
+       }
+        if (attr & ATTR_ACTCURS) {
+            HPEN oldpen;
+            oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
+            MoveToEx (hdc, startx, starty, NULL);
+            LineTo (hdc, startx+dx*length, starty+dy*length);
+            oldpen = SelectObject (hdc, oldpen);
+            DeleteObject (oldpen);
+        } else {
+            for (i = 0; i < length; i++) {
+                if (i % 2 == 0) {
+                    SetPixel(hdc, startx, starty, colours[23]);
+                }
+                startx += dx; starty += dy;
+            }
+        }
+    }
 }
 
 static int check_compose(int first, int second) {
@@ -1839,11 +1935,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
     int  scan, left_alt = 0, key_down, shift_state;
     int  r, i, code;
     unsigned char * p = output;
+    static int alt_state = 0;
+
+    HKL kbd_layout = GetKeyboardLayout(0);
 
     static WORD keys[3];
     static int compose_char = 0;
     static WPARAM compose_key = 0;
-
+    
     r = GetKeyboardState(keystate);
     if (!r) memset(keystate, 0, sizeof(keystate));
     else
@@ -1876,7 +1975,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                debug(("*"));
             debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
 
-            ch = MapVirtualKey(wParam, 2);
+            ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
             if (ch>=' ' && ch<='~') debug((", '%c'", ch));
             else if (ch)            debug((", $%02x", ch));
 
@@ -1905,11 +2004,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        }
 #endif
 
+       if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
+           keystate[VK_RMENU] = keystate[VK_MENU];
+       }
+
        /* Note if AltGr was pressed and if it was used as a compose key */
        if (cfg.compose_key) {
            if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
            {
-               keystate[VK_RMENU] = keystate[VK_MENU];
                if (!compose_state) compose_key = wParam;
            }
            if (wParam == VK_APPS && !compose_state)
@@ -1926,8 +2028,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            }
            else if (compose_state==1 && wParam != VK_CONTROL)
                compose_state = 0;
-       } else
+       } else {
            compose_state = 0;
+       }
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
        if ( (cfg.funky_type == 3 ||
@@ -2029,6 +2132,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
             return -1;
        }
        if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
+           alt_state = 0;
             PostMessage(hwnd, WM_CHAR, ' ', 0);
             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
             return -1;
@@ -2256,7 +2360,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        if(cfg.xlat_capslockcyr)
            keystate[VK_CAPITAL] = 0;
 
-       r = ToAscii (wParam, scan, keystate, keys, 0);
+       r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
        if(r>0)
        {
            p = output;
@@ -2305,7 +2409,6 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
     /* ALT alone may or may not want to bring up the System menu */
     if (wParam == VK_MENU) {
         if (cfg.alt_only) {
-            static int alt_state = 0;
             if (message == WM_SYSKEYDOWN)
                 alt_state = 1;
             else if (message == WM_SYSKEYUP && alt_state)