The WinSock library is now loaded at run-time, which means we can
[u/mdw/putty] / window.c
index 96ce4ad..5174b46 100644 (file)
--- a/window.c
+++ b/window.c
@@ -1,23 +1,3 @@
-#include <windows.h>
-#include <imm.h>
-#include <commctrl.h>
-#include <richedit.h>
-#include <mmsystem.h>
-#ifndef AUTO_WINSOCK
-#ifdef WINSOCK_TWO
-#include <winsock2.h>
-#else
-#include <winsock.h>
-#endif
-#endif
-
-#ifndef NO_MULTIMON
-#if WINVER < 0x0500
-#define COMPILE_MULTIMON_STUBS
-#include <multimon.h>
-#endif
-#endif
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
 #define PUTTY_DO_GLOBALS              /* actually _define_ globals */
 #include "putty.h"
 #include "terminal.h"
-#include "winstuff.h"
 #include "storage.h"
 #include "win_res.h"
 
+#ifndef NO_MULTIMON
+#if WINVER < 0x0500
+#define COMPILE_MULTIMON_STUBS
+#include <multimon.h>
+#endif
+#endif
+
+#include <imm.h>
+#include <commctrl.h>
+#include <richedit.h>
+#include <mmsystem.h>
+
 #define IDM_SHOWLOG   0x0010
 #define IDM_NEWSESS   0x0020
 #define IDM_DUPSESS   0x0030
@@ -55,6 +46,7 @@
 
 #define WM_IGNORE_CLIP (WM_XUSER + 2)
 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
+#define WM_AGENT_CALLBACK (WM_XUSER + 4)
 
 /* Needed for Chinese support and apparently not always defined. */
 #ifndef VK_PROCESSKEY
@@ -74,6 +66,7 @@ static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        unsigned char *output);
 static void cfgtopalette(void);
+static void systopalette(void);
 static void init_palette(void);
 static void init_fonts(int, int);
 static void another_font(int);
@@ -122,6 +115,13 @@ Config cfg;                               /* exported to windlg.c */
 
 extern struct sesslist sesslist;       /* imported from windlg.c */
 
+struct agent_callback {
+    void (*callback)(void *, void *, int);
+    void *callback_ctx;
+    void *data;
+    int len;
+};
+
 #define FONT_NORMAL 0
 #define FONT_BOLD 1
 #define FONT_UNDERLINE 2
@@ -169,10 +169,6 @@ static char *window_name, *icon_name;
 
 static int compose_state = 0;
 
-static int wsa_started = 0;
-
-static OSVERSIONINFO osVersion;
-
 static UINT wm_mousewheel = WM_MOUSEWHEEL;
 
 /* Dummy routine, only required in plink. */
@@ -182,9 +178,6 @@ void ldisc_update(void *frontend, int echo, int edit)
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
-    static char appname[] = "PuTTY";
-    WORD winsock_ver;
-    WSADATA wsadata;
     WNDCLASS wndclass;
     MSG msg;
     int guess_width, guess_height;
@@ -192,20 +185,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     hinst = inst;
     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
 
-    winsock_ver = MAKEWORD(1, 1);
-    if (WSAStartup(winsock_ver, &wsadata)) {
-       MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
-                  MB_OK | MB_ICONEXCLAMATION);
-       return 1;
-    }
-    if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
-       MessageBox(NULL, "WinSock version is incompatible with 1.1",
-                  "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
-       WSACleanup();
-       return 1;
-    }
-    wsa_started = 1;
-    /* WISHLIST: maybe allow config tweaking even if winsock not present? */
     sk_init();
 
     InitCommonControls();
@@ -214,14 +193,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * config box. */
     defuse_showwindow();
 
+    if (!init_winver())
     {
-       ZeroMemory(&osVersion, sizeof(osVersion));
-       osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
-       if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
-            MessageBox(NULL, "Windows refuses to report a version",
-                       "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
-           return 1;
-        }
+       char *str = dupprintf("%s Fatal Error", appname);
+       MessageBox(NULL, "Windows refuses to report a version",
+                  str, MB_OK | MB_ICONEXCLAMATION);
+       sfree(str);
+       return 1;
     }
 
     /*
@@ -300,8 +278,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            p[i] = '\0';
            do_defaults(p + 1, &cfg);
            if (!*cfg.host && !do_config()) {
-               WSACleanup();
-               return 0;
+               cleanup_exit(0);
            }
        } else if (*p == '&') {
            /*
@@ -319,8 +296,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                UnmapViewOfFile(cp);
                CloseHandle(filemap);
            } else if (!do_config()) {
-               WSACleanup();
-               return 0;
+               cleanup_exit(0);
            }
        } else {
            /*
@@ -350,18 +326,21 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                     * 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"
+                   char *s1, *s2;
+                   s1 = dupprintf("This procedure will remove ALL Registry\n"
+                                  "entries associated with %s, and will\n"
+                                  "also remove the random seed file.\n"
                                   "\n"
                                   "THIS PROCESS WILL DESTROY YOUR SAVED\n"
                                   "SESSIONS. Are you really sure you want\n"
-                                  "to continue?",
-                                  "PuTTY Warning",
+                                  "to continue?", appname);
+                   s2 = dupprintf("%s Warning", appname);
+                   if (MessageBox(NULL, s1, s2,
                                   MB_YESNO | MB_ICONWARNING) == IDYES) {
                        cleanup_all();
                    }
+                   sfree(s1);
+                   sfree(s2);
                    exit(0);
                } else if (*p != '-') {
                    char *q = p;
@@ -422,8 +401,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        cmdline_run_saved(&cfg);
 
        if (!*cfg.host && !do_config()) {
-           WSACleanup();
-           return 0;
+           cleanup_exit(0);
        }
 
        /*
@@ -481,19 +459,21 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                break;
            }
        if (back == NULL) {
+           char *str = dupprintf("%s Internal Error", appname);
            MessageBox(NULL, "Unsupported protocol number found",
-                      "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
-           WSACleanup();
-           return 1;
+                      str, MB_OK | MB_ICONEXCLAMATION);
+           sfree(str);
+           cleanup_exit(1);
        }
     }
 
     /* Check for invalid Port number (i.e. zero) */
     if (cfg.port == 0) {
+       char *str = dupprintf("%s Internal Error", appname);
        MessageBox(NULL, "Invalid Port Number",
-                  "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
-       WSACleanup();
-       return 1;
+                  str, MB_OK | MB_ICONEXCLAMATION);
+       sfree(str);
+       cleanup_exit(1);
     }
 
     if (!prev) {
@@ -620,24 +600,26 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * Start up the telnet connection.
      */
     {
-       char *error;
+       const char *error;
        char msg[1024], *title;
        char *realhost;
 
-       error = back->init((void *)term, &backhandle, &cfg,
+       error = back->init(NULL, &backhandle, &cfg,
                           cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
        back->provide_logctx(backhandle, logctx);
        if (error) {
+           char *str = dupprintf("%s Error", appname);
            sprintf(msg, "Unable to open connection to\n"
                    "%.800s\n" "%s", cfg.host, error);
-           MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
+           MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
+           sfree(str);
            return 0;
        }
        window_name = icon_name = NULL;
        if (*cfg.wintitle) {
            title = cfg.wintitle;
        } else {
-           sprintf(msg, "%s - PuTTY", realhost);
+           sprintf(msg, "%s - %s", realhost, appname);
            title = msg;
        }
        sfree(realhost);
@@ -671,10 +653,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        HMENU m = GetSystemMenu(hwnd, FALSE);
        HMENU s;
        int i;
+       char *str;
 
        AppendMenu(m, MF_SEPARATOR, 0, 0);
        specials_menu_position = GetMenuItemCount(m);
-       debug(("specials_menu_position = %d\n", specials_menu_position));
        AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
        AppendMenu(m, MF_SEPARATOR, 0, 0);
        AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
@@ -698,7 +680,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        AppendMenu(m, MF_SEPARATOR, 0, 0);
         if (help_path)
             AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
-       AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
+       str = dupprintf("&About %s", appname);
+       AppendMenu(m, MF_ENABLED, IDM_ABOUT, str);
+       sfree(str);
     }
 
     update_specials_menu(NULL);
@@ -824,8 +808,6 @@ void cleanup_exit(int code)
     if (pal)
        DeleteObject(pal);
     sk_cleanup();
-    if (wsa_started)
-       WSACleanup();
 
     if (cfg.protocol == PROT_SSH) {
        random_save_seed();
@@ -852,8 +834,8 @@ char *do_select(SOCKET skt, int startup)
     }
     if (!hwnd)
        return "do_select(): internal error (hwnd==NULL)";
-    if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
-       switch (WSAGetLastError()) {
+    if (p_WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
+       switch (p_WSAGetLastError()) {
          case WSAENETDOWN:
            return "Network is down";
          default:
@@ -910,18 +892,22 @@ void set_raw_mouse_mode(void *frontend, int activate)
 void connection_fatal(void *frontend, char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
+    sprintf(morestuff, "%.70s Fatal Error", appname);
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
+    sfree(stuff);
+
     if (cfg.close_on_exit == FORCE_ON)
        PostQuitMessage(1);
     else {
        session_closed = TRUE;
-       set_icon(NULL, "PuTTY (inactive)");
-       set_title(NULL, "PuTTY (inactive)");
+       sprintf(morestuff, "%.70s (inactive)", appname);
+       set_icon(NULL, morestuff);
+       set_title(NULL, morestuff);
     }
 }
 
@@ -931,12 +917,14 @@ void connection_fatal(void *frontend, char *fmt, ...)
 void cmdline_error(char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK);
+    sprintf(morestuff, "%.70s Command Line Error", appname);
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
+    sfree(stuff);
     exit(1);
 }
 
@@ -964,11 +952,13 @@ static void enact_pending_netevent(void)
        if (cfg.close_on_exit == FORCE_ON ||
            cfg.close_on_exit == AUTO) PostQuitMessage(0);
        else {
+           char morestuff[100];
            session_closed = TRUE;
-           set_icon(NULL, "PuTTY (inactive)");
-           set_title(NULL, "PuTTY (inactive)");
+           sprintf(morestuff, "%.70s (inactive)", appname);
+           set_icon(NULL, morestuff);
+           set_title(NULL, morestuff);
            MessageBox(hwnd, "Connection closed by remote host",
-                      "PuTTY", MB_OK | MB_ICONINFORMATION);
+                      appname, MB_OK | MB_ICONINFORMATION);
        }
     }
 }
@@ -992,6 +982,38 @@ static void cfgtopalette(void)
        defpal[i].rgbtGreen = cfg.colours[w][1];
        defpal[i].rgbtBlue = cfg.colours[w][2];
     }
+
+    /* Override with system colours if appropriate */
+    if (cfg.system_colour)
+        systopalette();
+}
+
+/*
+ * Override bit of defpal with colours from the system.
+ * (NB that this takes a copy the system colours at the time this is called,
+ * so subsequent colour scheme changes don't take effect. To fix that we'd
+ * probably want to be using GetSysColorBrush() and the like.)
+ */
+static void systopalette(void)
+{
+    int i;
+    static const struct { int nIndex; int norm; int bold; } or[] =
+    {
+       { COLOR_WINDOWTEXT,     16, 17 }, /* Default Foreground */
+       { COLOR_WINDOW,         18, 19 }, /* Default Background */
+       { COLOR_HIGHLIGHTTEXT,  20, 21 }, /* Cursor Text */
+       { COLOR_HIGHLIGHT,      22, 23 }, /* Cursor Colour */
+    };
+
+    for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) {
+       COLORREF colour = GetSysColor(or[i].nIndex);
+       defpal[or[i].norm].rgbtRed =
+          defpal[or[i].bold].rgbtRed = GetRValue(colour);
+       defpal[or[i].norm].rgbtGreen =
+          defpal[or[i].bold].rgbtGreen = GetGValue(colour);
+       defpal[or[i].norm].rgbtBlue =
+          defpal[or[i].bold].rgbtBlue = GetBValue(colour);
+    }
 }
 
 /*
@@ -1673,13 +1695,17 @@ 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",
-                      MB_ICONWARNING | MB_OKCANCEL) == IDOK)
-           DestroyWindow(hwnd);
+       {
+           char *str;
+           show_mouseptr(1);
+           str = dupprintf("%s Exit Confirmation", appname);
+           if (!cfg.warn_on_close || session_closed ||
+               MessageBox(hwnd,
+                          "Are you sure you want to close this session?",
+                          str, MB_ICONWARNING | MB_OKCANCEL) == IDOK)
+               DestroyWindow(hwnd);
+           sfree(str);
+       }
        return 0;
       case WM_DESTROY:
        show_mouseptr(1);
@@ -2554,6 +2580,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            return TRUE;
        }
+       break;
+      case WM_AGENT_CALLBACK:
+       {
+           struct agent_callback *c = (struct agent_callback *)lParam;
+           c->callback(c->callback_ctx, c->data, c->len);
+           sfree(c);
+       }
+       return 0;
       default:
        if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
            int shift_pressed=0, control_pressed=0;
@@ -3497,6 +3531,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            *p++ = 0x1C;
            return p - output;
        }
+       if (shift_state == 3 && wParam == 0xDE) {
+           *p++ = 0x1E;               /* Ctrl-~ == Ctrl-^ in xterm at least */
+           return p - output;
+       }
        if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
            *p++ = '\r';
            *p++ = '\n';
@@ -3832,9 +3870,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                            ldisc_send(ldisc, &ch, 1, 1);
                        }
                        alt_sum = 0;
-                   } else
+                   } else {
                        term_seen_key_event(term);
                        lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
+                   }
                } else {
                    if(capsOn && ch < 0x80) {
                        WCHAR cbuf[2];
@@ -4265,12 +4304,14 @@ void optimised_move(void *frontend, int to, int from, int lines)
 void fatalbox(char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
+    sprintf(morestuff, "%.70s Fatal Error", appname);
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
+    sfree(stuff);
     cleanup_exit(1);
 }
 
@@ -4280,13 +4321,15 @@ void fatalbox(char *fmt, ...)
 void modalfatalbox(char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Fatal Error",
+    sprintf(morestuff, "%.70s Fatal Error", appname);
+    MessageBox(hwnd, stuff, morestuff,
               MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
+    sfree(stuff);
     cleanup_exit(1);
 }
 
@@ -4354,12 +4397,31 @@ void beep(void *frontend, int mode)
        if (!PlaySound(cfg.bell_wavefile.path, NULL,
                       SND_ASYNC | SND_FILENAME)) {
            char buf[sizeof(cfg.bell_wavefile) + 80];
+           char otherbuf[100];
            sprintf(buf, "Unable to play sound file\n%s\n"
                    "Using default sound instead", cfg.bell_wavefile);
-           MessageBox(hwnd, buf, "PuTTY Sound Error",
+           sprintf(otherbuf, "%.70s Sound Error", appname);
+           MessageBox(hwnd, buf, otherbuf,
                       MB_OK | MB_ICONEXCLAMATION);
            cfg.beep = BELL_DEFAULT;
        }
+    } else if (mode == BELL_PCSPEAKER) {
+       static long lastbeep = 0;
+       long beepdiff;
+
+       beepdiff = GetTickCount() - lastbeep;
+       if (beepdiff >= 0 && beepdiff < 50)
+           return;
+
+       /*
+        * We must beep in different ways depending on whether this
+        * is a 95-series or NT-series OS.
+        */
+       if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
+           Beep(800, 100);
+       else
+           MessageBeep(-1);
+       lastbeep = GetTickCount();
     }
     /* Otherwise, either visual bell or disabled; do nothing here */
     if (!term->has_focus) {
@@ -4596,3 +4658,19 @@ void frontend_keypress(void *handle)
      */
     return;
 }
+
+int from_backend(void *frontend, int is_stderr, const char *data, int len)
+{
+    return term_data(term, is_stderr, data, len);
+}
+
+void agent_schedule_callback(void (*callback)(void *, void *, int),
+                            void *callback_ctx, void *data, int len)
+{
+    struct agent_callback *c = snew(struct agent_callback);
+    c->callback = callback;
+    c->callback_ctx = callback_ctx;
+    c->data = data;
+    c->len = len;
+    PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);
+}