Miscellaneous fixes to try to make other compilers happier
[u/mdw/putty] / window.c
index 408a9b6..7b7dee1 100644 (file)
--- a/window.c
+++ b/window.c
@@ -1,12 +1,19 @@
 #include <windows.h>
 #include <commctrl.h>
+#ifndef AUTO_WINSOCK
+#ifdef WINSOCK_TWO
+#include <winsock2.h>
+#else
 #include <winsock.h>
+#endif
+#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 
 #define PUTTY_DO_GLOBALS                      /* actually _define_ globals */
 #include "putty.h"
+#include "storage.h"
 #include "win_res.h"
 
 #define IDM_SHOWLOG   0x0010
@@ -59,6 +66,7 @@ static void enact_pending_netevent(void);
 #define FONT_OEMBOLDUND 6
 #define FONT_OEMUND 7
 static HFONT fonts[8];
+static int font_needs_hand_underlining;
 static enum {
     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
 } bold_mode;
@@ -75,11 +83,19 @@ static RGBTRIPLE defpal[NCOLOURS];
 
 static HWND hwnd;
 
+static HBITMAP caretbm;
+
 static int dbltime, lasttime, lastact;
 static Mouse_Button lastbtn;
 
 static char *window_name, *icon_name;
 
+static Ldisc *real_ldisc;
+
+void begin_session(void) {
+    ldisc = real_ldisc;
+}
+
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     static char appname[] = "PuTTY";
     WORD winsock_ver;
@@ -89,7 +105,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     int guess_width, guess_height;
 
     putty_inst = inst;
-    flags = FLAG_VERBOSE | FLAG_WINDOWED | FLAG_CONNECTION;
+    flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
 
     winsock_ver = MAKEWORD(1, 1);
     if (WSAStartup(winsock_ver, &wsadata)) {
@@ -116,7 +132,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        default_protocol = DEFAULT_PROTOCOL;
        default_port = DEFAULT_PORT;
 
-       do_defaults(NULL);
+       do_defaults(NULL, &cfg);
 
        p = cmdline;
        while (*p && isspace(*p)) p++;
@@ -140,6 +156,32 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                tolower(p[1]) == 'o' &&
                tolower(p[2]) == 'g') {
                 logfile = "putty.log";
+           } else if (q == p + 7 &&
+               tolower(p[0]) == 'c' &&
+               tolower(p[1]) == 'l' &&
+               tolower(p[2]) == 'e' &&
+               tolower(p[3]) == 'a' &&
+               tolower(p[4]) == 'n' &&
+               tolower(p[5]) == 'u' &&
+               tolower(p[6]) == 'p') {
+                /*
+                 * `putty -cleanup'. Remove all registry entries
+                 * associated with PuTTY, and also find and delete
+                 * the random seed file.
+                 */
+                if (MessageBox(NULL,
+                               "This procedure will remove ALL Registry\n"
+                               "entries associated with PuTTY, and will\n"
+                               "also remove the PuTTY random seed file.\n"
+                               "\n"
+                               "THIS PROCESS WILL DESTROY YOUR SAVED\n"
+                               "SESSIONS. Are you really sure you want\n"
+                               "to continue?",
+                               "PuTTY Warning",
+                               MB_YESNO | MB_ICONWARNING) == IDYES) {
+                    cleanup_all();
+                }
+                exit(0);
            }
            p = q + strspn(q, " \t");
        }
@@ -148,7 +190,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         * An initial @ means to activate a saved session.
         */
        if (*p == '@') {
-           do_defaults (p+1);
+           do_defaults (p+1, &cfg);
            if (!*cfg.host && !do_config()) {
                WSACleanup();
                return 0;
@@ -215,6 +257,19 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                return 0;
            }
        }
+
+       /* See if host is of the form user@host */
+       if (cfg.host[0] != '\0') {
+           char *atsign = strchr(cfg.host, '@');
+           /* Make sure we're not overflowing the user field */
+           if (atsign) {
+               if (atsign-cfg.host < sizeof cfg.username) {
+                   strncpy (cfg.username, cfg.host, atsign-cfg.host);
+                   cfg.username[atsign-cfg.host] = '\0';
+               }
+               memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
+           }
+       }
     }
 
     /*
@@ -237,7 +292,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         }
     }
 
-    ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
+    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;
 
     if (!prev) {
        wndclass.style         = 0;
@@ -327,6 +385,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                  SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
 
     /*
+     * Set up a caret bitmap, with no content.
+     */
+    {
+        char *bits;
+        int size = (font_width+15)/16 * 2 * font_height; 
+        bits = calloc(size, 1);
+        caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
+        free(bits);
+    }
+
+    /*
      * Initialise the scroll bar.
      */
     {
@@ -346,7 +415,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
      */
     {
        char *error;
-       char msg[1024];
+       char msg[1024], *title;
        char *realhost;
 
        error = back->init (hwnd, cfg.host, cfg.port, &realhost);
@@ -356,9 +425,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
            return 0;
        }
        window_name = icon_name = NULL;
-       sprintf(msg, "%s - PuTTY", realhost);
-       set_title (msg);
-       set_icon (msg);
+        if (*cfg.wintitle) {
+            title = cfg.wintitle;
+        } else {
+            sprintf(msg, "%s - PuTTY", realhost);
+            title = msg;
+        }
+       set_title (title);
+       set_icon (title);
     }
 
     session_closed = FALSE;
@@ -480,9 +554,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                    KillTimer(hwnd, timer_id);
                    timer_id = 0;
                }
+                HideCaret(hwnd);
                if (inbuf_head)
                    term_out();
                term_update();
+                ShowCaret(hwnd);
                if (!has_focus)
                   timer_id = SetTimer(hwnd, 1, 2000, NULL);
                else if (cfg.blinktext)
@@ -519,12 +595,39 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
 }
 
 /*
+ * Print a message box and close the connection.
+ */
+void connection_fatal(char *fmt, ...) {
+    va_list ap;
+    char stuff[200];
+
+    va_start(ap, fmt);
+    vsprintf(stuff, fmt, ap);
+    va_end(ap);
+    MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
+    if (cfg.close_on_exit)
+        PostQuitMessage(1);
+    else {
+        session_closed = TRUE;
+        SetWindowText (hwnd, "PuTTY (inactive)");
+    }
+}
+
+/*
  * Actually do the job requested by a WM_NETEVENT
  */
 static void enact_pending_netevent(void) {
     int i;
+    static int reentering = 0;
+
+    if (reentering)
+        return;                        /* don't unpend the pending */
+
     pending_netevent = FALSE;
+
+    reentering = 1;
     i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
+    reentering = 0;
 
     if (i < 0) {
        char buf[1024];
@@ -536,10 +639,9 @@ static void enact_pending_netevent(void) {
            sprintf(buf, "Unexpected network error %d", -i);
            break;
        }
-       MessageBox(hwnd, buf, "PuTTY Fatal Error",
-                  MB_ICONERROR | MB_OK);
-       PostQuitMessage(1);
-    } else if (i == 0) {
+        connection_fatal(buf);
+    }
+    if (i <= 0) {
        if (cfg.close_on_exit)
            PostQuitMessage(0);
        else {
@@ -643,7 +745,7 @@ font_messup:
 
     if (cfg.fontisbold) {
        fw_dontcare = FW_BOLD;
-       fw_bold = FW_BLACK;
+       fw_bold = FW_HEAVY;
    } else {
        fw_dontcare = FW_DONTCARE;
        fw_bold = FW_BOLD;
@@ -670,6 +772,50 @@ font_messup:
 
        f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
 
+        /*
+         * Some fonts, e.g. 9-pt Courier, draw their underlines
+         * outside their character cell. We successfully prevent
+         * screen corruption by clipping the text output, but then
+         * we lose the underline completely. Here we try to work
+         * out whether this is such a font, and if it is, we set a
+         * flag that causes underlines to be drawn by hand.
+         *
+         * Having tried other more sophisticated approaches (such
+         * as examining the TEXTMETRIC structure or requesting the
+         * height of a string), I think we'll do this the brute
+         * force way: we create a small bitmap, draw an underlined
+         * space on it, and test to see whether any pixels are
+         * foreground-coloured. (Since we expect the underline to
+         * go all the way across the character cell, we only search
+         * down a single column of the bitmap, half way across.)
+         */
+        {
+            HDC und_dc;
+            HBITMAP und_bm, und_oldbm;
+            int i, gotit;
+            COLORREF c;
+
+            und_dc = CreateCompatibleDC(hdc);
+            und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
+            und_oldbm = SelectObject(und_dc, und_bm);
+            SelectObject(und_dc, fonts[FONT_UNDERLINE]);
+            SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
+            SetTextColor (und_dc, RGB(255,255,255));
+            SetBkColor (und_dc, RGB(0,0,0));
+            SetBkMode (und_dc, OPAQUE);
+            ExtTextOut (und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
+            gotit = FALSE;
+            for (i = 0; i < font_height; i++) {
+                c = GetPixel(und_dc, font_width/2, i);
+                if (c != RGB(0,0,0))
+                    gotit = TRUE;
+            }
+            SelectObject(und_dc, und_oldbm);
+            DeleteObject(und_bm);
+            DeleteDC(und_dc);
+            font_needs_hand_underlining = !gotit;
+        }
+
         if (bold_mode == BOLD_FONT) {
            f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
            f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
@@ -856,6 +1002,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
     static int ignore_size = FALSE;
     static int ignore_clip = FALSE;
     static int just_reconfigged = FALSE;
+    static int resizing = FALSE;
 
     switch (message) {
       case WM_TIMER:
@@ -863,7 +1010,9 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            enact_pending_netevent();
        if (inbuf_head)
            term_out();
+        HideCaret(hwnd);
        term_update();
+        ShowCaret(hwnd);
        return 0;
       case WM_CREATE:
        break;
@@ -1116,6 +1265,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
       case WM_PAINT:
        {
            PAINTSTRUCT p;
+            HideCaret(hwnd);
            hdc = BeginPaint (hwnd, &p);
            if (pal) {
                SelectPalette (hdc, pal, TRUE);
@@ -1126,6 +1276,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            SelectObject (hdc, GetStockObject(SYSTEM_FONT));
            SelectObject (hdc, GetStockObject(WHITE_PEN));
            EndPaint (hwnd, &p);
+            ShowCaret(hwnd);
        }
        return 0;
       case WM_NETEVENT:
@@ -1142,11 +1293,14 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        return 0;
       case WM_SETFOCUS:
        has_focus = TRUE;
+        CreateCaret(hwnd, caretbm, 0, 0);
+        ShowCaret(hwnd);
        term_out();
        term_update();
        break;
       case WM_KILLFOCUS:
        has_focus = FALSE;
+        DestroyCaret();
        term_out();
        term_update();
        break;
@@ -1155,9 +1309,12 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        break;
       case WM_ENTERSIZEMOVE:
         EnableSizeTip(1);
+        resizing = TRUE;
         break;
       case WM_EXITSIZEMOVE:
         EnableSizeTip(0);
+        resizing = FALSE;
+        back->size();
         break;
       case WM_SIZING:
        {
@@ -1226,7 +1383,13 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            if (w != cols || h != rows || just_reconfigged) {
                term_invalidate();
                term_size (h, w, cfg.savelines);
-               back->size();
+                /*
+                 * Don't call back->size in mid-resize. (To prevent
+                 * massive numbers of resize events getting sent
+                 * down the connection during an NT opaque drag.)
+                 */
+                if (!resizing)
+                    back->size();
                just_reconfigged = FALSE;
            }
        }
@@ -1312,6 +1475,16 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 }
 
 /*
+ * Move the system caret. (We maintain one, even though it's
+ * invisible, for the benefit of blind people: apparently some
+ * helper software tracks the system caret, so we should arrange to
+ * have one.)
+ */
+void sys_cursor(int x, int y) {
+    SetCaretPos(x * font_width, y * font_height);
+}
+
+/*
  * Draw a line of text in the window, at given character
  * coordinates, in given attributes.
  *
@@ -1325,7 +1498,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
     RECT line_box;
     int force_manual_underline = 0;
     int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
-static int *IpDx = 0, IpDxLEN = 0;;
+    static int *IpDx = 0, IpDxLEN = 0;;
 
     if (len>IpDxLEN || IpDx[0] != fnt_width) {
        int i;
@@ -1468,6 +1641,8 @@ static int *IpDx = 0, IpDxLEN = 0;;
 
        nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
     }
+    if (font_needs_hand_underlining && (attr & ATTR_UNDER))
+        force_manual_underline = 1;
     if (attr & ATTR_REVERSE) {
        t = nfg; nfg = nbg; nbg = t;
     }
@@ -1583,10 +1758,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned cha
     int  r, i, code;
     unsigned char * p = output;
 
-static WORD keys[3];
-static int compose_state = 0;
-static int compose_char = 0;
-static WPARAM compose_key = 0;
+    static WORD keys[3];
+    static int compose_state = 0;
+    static int compose_char = 0;
+    static WPARAM compose_key = 0;
 
     r = GetKeyboardState(keystate);
     if (!r) memset(keystate, 0, sizeof(keystate));
@@ -1695,19 +1870,25 @@ static WPARAM compose_key = 0;
 
        /* Lets see if it's a pattern we know all about ... */
        if (wParam == VK_PRIOR && shift_state == 1) {
-          SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
-          return 0;
+            SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
+            return 0;
        }
        if (wParam == VK_NEXT && shift_state == 1) {
-          SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
-          return 0;
+            SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
+            return 0;
        }
+        if (wParam == VK_INSERT && shift_state == 1) {
+            term_mouse (MB_PASTE, MA_CLICK, 0, 0);
+            term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
+            return 0;
+        }
        if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
-          return -1;
+            return -1;
        }
        if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
-          SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
-          return -1;
+            
+            SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
+            return -1;
        }
 
        /* Nethack keypad */
@@ -1731,7 +1912,7 @@ static WPARAM compose_key = 0;
 
           if ( cfg.funky_type == 0 ||
              ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
-              case VK_EXECUTE: xkey = 'P'; break;
+              case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
               case VK_DIVIDE:  xkey = 'Q'; break;
               case VK_MULTIPLY:xkey = 'R'; break;
               case VK_SUBTRACT:xkey = 'S'; break;
@@ -1891,6 +2072,16 @@ static WPARAM compose_key = 0;
                return p - output;
            }
        }
+
+       /*
+        * Finally, deal with Return ourselves. (Win95 seems to
+        * foul it up when Alt is pressed, for some reason.)
+        */
+       if (wParam == VK_RETURN)       /* Return */
+       {
+           *p++ = 0x0D;
+           return p-output;
+       }
     }
 
     /* Okay we've done everything interesting; let windows deal with 
@@ -1949,13 +2140,11 @@ static WPARAM compose_key = 0;
     }
 
     /* This stops ALT press-release doing a 'COMMAND MENU' function */
-#if 0
     if (message == WM_SYSKEYUP && wParam == VK_MENU) 
     {
        keystate[VK_MENU] = 0;
        return 0;
     }
-#endif
 
     return -1;
 }
@@ -2063,7 +2252,7 @@ void palette_reset (void) {
     }
 }
 
-void write_clip (void *data, int len) {
+void write_clip (void *data, int len, int must_deselect) {
     HGLOBAL clipdata;
     void *lock;
 
@@ -2077,14 +2266,18 @@ void write_clip (void *data, int len) {
     ((unsigned char *) lock) [len] = 0;
     GlobalUnlock (clipdata);
 
-    SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
+    if (!must_deselect)
+        SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
+
     if (OpenClipboard (hwnd)) {
        EmptyClipboard();
        SetClipboardData (CF_TEXT, clipdata);
        CloseClipboard();
     } else
        GlobalFree (clipdata);
-    SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
+
+    if (!must_deselect)
+        SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
 }
 
 void get_clip (void **p, int *len) {