The WinSock library is now loaded at run-time, which means we can
[u/mdw/putty] / window.c
index ac8ac85..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
 #define IDM_RECONF    0x0040
 #define IDM_CLRSB     0x0050
 #define IDM_RESET     0x0060
-#define IDM_TEL_AYT   0x0070
-#define IDM_TEL_BRK   0x0080
-#define IDM_TEL_SYNCH 0x0090
-#define IDM_TEL_EC    0x00a0
-#define IDM_TEL_EL    0x00b0
-#define IDM_TEL_GA    0x00c0
-#define IDM_TEL_NOP   0x00d0
-#define IDM_TEL_ABORT 0x00e0
-#define IDM_TEL_AO    0x00f0
-#define IDM_TEL_IP    0x0100
-#define IDM_TEL_SUSP  0x0110
-#define IDM_TEL_EOR   0x0120
-#define IDM_TEL_EOF   0x0130
 #define IDM_HELP      0x0140
 #define IDM_ABOUT     0x0150
 #define IDM_SAVEDSESS 0x0160
 #define IDM_SESSLGP   0x0250          /* log type printable */
 #define IDM_SESSLGA   0x0260          /* log type all chars */
 #define IDM_SESSLGE   0x0270          /* log end */
+
+#define IDM_SPECIAL_MIN 0x0400
+#define IDM_SPECIAL_MAX 0x0800
+
 #define IDM_SAVED_MIN 0x1000
 #define IDM_SAVED_MAX 0x2000
 
 #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
 #define WHEEL_DELTA 120
 #endif
 
+static Mouse_Button translate_button(Mouse_Button button);
 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);
@@ -123,10 +108,20 @@ static void *backhandle;
 static struct unicode_data ucsdata;
 static int session_closed;
 
+static const struct telnet_special *specials;
+static int specials_menu_position;
+
 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
@@ -174,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. */
@@ -187,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;
@@ -197,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();
@@ -219,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;
     }
 
     /*
@@ -272,8 +245,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        char *p;
        int got_host = 0;
 
-       default_protocol = DEFAULT_PROTOCOL;
-       default_port = DEFAULT_PORT;
+       default_protocol = be_default_protocol;
+       /* Find the appropriate default port. */
+       {
+           int i;
+           default_port = 0; /* illegal */
+           for (i = 0; backends[i].backend != NULL; i++)
+               if (backends[i].protocol == default_protocol) {
+                   default_port = backends[i].backend->default_port;
+                   break;
+               }
+       }
        cfg.logtype = LGTYP_NONE;
 
        do_defaults(NULL, &cfg);
@@ -296,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 == '&') {
            /*
@@ -315,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 {
            /*
@@ -346,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;
@@ -418,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);
        }
 
        /*
@@ -477,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) {
@@ -590,7 +574,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     {
        char *bits;
        int size = (font_width + 15) / 16 * 2 * font_height;
-       bits = smalloc(size);
+       bits = snewn(size, char);
        memset(bits, 0, size);
        caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
        sfree(bits);
@@ -616,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);
@@ -665,32 +651,12 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      */
     {
        HMENU m = GetSystemMenu(hwnd, FALSE);
-       HMENU p, s;
+       HMENU s;
        int i;
+       char *str;
 
        AppendMenu(m, MF_SEPARATOR, 0, 0);
-       if (cfg.protocol == PROT_TELNET) {
-           p = CreateMenu();
-           AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
-           AppendMenu(p, MF_SEPARATOR, 0, 0);
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
-           AppendMenu(p, MF_SEPARATOR, 0, 0);
-           AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
-           AppendMenu(p, MF_SEPARATOR, 0, 0);
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
-           AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
-                      "Telnet Command");
-           AppendMenu(m, MF_SEPARATOR, 0, 0);
-       }
+       specials_menu_position = GetMenuItemCount(m);
        AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
        AppendMenu(m, MF_SEPARATOR, 0, 0);
        AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
@@ -714,9 +680,13 @@ 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);
+
     /*
      * Set up the initial input locale.
      */
@@ -778,9 +748,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            if (pending_netevent) {
                enact_pending_netevent();
 
-               /* Force the cursor blink on */
-               term_blink(term, 1);
-
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                    continue;
            }
@@ -841,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();
@@ -869,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:
@@ -881,6 +846,37 @@ char *do_select(SOCKET skt, int startup)
 }
 
 /*
+ * Update the Special Commands submenu.
+ */
+void update_specials_menu(void *frontend)
+{
+    HMENU m = GetSystemMenu(hwnd, FALSE);
+    int menu_already_exists = (specials != NULL);
+    int i;
+
+    specials = back->get_specials(backhandle);
+    if (specials) {
+       HMENU p = CreateMenu();
+       for (i = 0; specials[i].name; i++) {
+           assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
+           if (*specials[i].name)
+               AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
+                          specials[i].name);
+           else
+               AppendMenu(p, MF_SEPARATOR, 0, 0);
+       }
+       if (menu_already_exists)
+           DeleteMenu(m, specials_menu_position, MF_BYPOSITION);
+       else
+           InsertMenu(m, specials_menu_position,
+                      MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+       InsertMenu(m, specials_menu_position,
+                  MF_BYPOSITION | MF_POPUP | MF_ENABLED,
+                  (UINT) p, "Special Command");
+    }
+}
+
+/*
  * set or clear the "raw mouse message" mode
  */
 void set_raw_mouse_mode(void *frontend, int activate)
@@ -896,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);
-    if (cfg.close_on_exit == COE_ALWAYS)
+    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);
     }
 }
 
@@ -917,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);
 }
 
@@ -947,14 +949,16 @@ static void enact_pending_netevent(void)
     if (ret == 0 && !session_closed) {
        /* Abnormal exits will already have set session_closed and taken
         * appropriate action. */
-       if (cfg.close_on_exit == COE_ALWAYS ||
-           cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
+       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);
        }
     }
 }
@@ -978,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);
+    }
 }
 
 /*
@@ -989,6 +1025,10 @@ static void init_palette(void)
     HDC hdc = GetDC(hwnd);
     if (hdc) {
        if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
+           /*
+            * This is a genuine case where we must use smalloc
+            * because the snew macros can't cope.
+            */
            logpal = smalloc(sizeof(*logpal)
                             - sizeof(logpal->palPalEntry)
                             + NCOLOURS * sizeof(PALETTEENTRY));
@@ -1052,7 +1092,7 @@ static void init_fonts(int pick_width, int pick_height)
     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
     und_mode = UND_FONT;
 
-    if (cfg.fontisbold) {
+    if (cfg.font.isbold) {
        fw_dontcare = FW_BOLD;
        fw_bold = FW_HEAVY;
     } else {
@@ -1065,7 +1105,7 @@ static void init_fonts(int pick_width, int pick_height)
     if (pick_height)
        font_height = pick_height;
     else {
-       font_height = cfg.fontheight;
+       font_height = cfg.font.height;
        if (font_height > 0) {
            font_height =
                -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
@@ -1077,9 +1117,9 @@ static void init_fonts(int pick_width, int pick_height)
     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
                           c, OUT_DEFAULT_PRECIS, \
                           CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
-                          FIXED_PITCH | FF_DONTCARE, cfg.font)
+                          FIXED_PITCH | FF_DONTCARE, cfg.font.name)
 
-    f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
+    f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);
 
     lfont.lfHeight = font_height;
     lfont.lfWidth = font_width;
@@ -1089,12 +1129,12 @@ static void init_fonts(int pick_width, int pick_height)
     lfont.lfItalic = FALSE;
     lfont.lfUnderline = FALSE;
     lfont.lfStrikeOut = FALSE;
-    lfont.lfCharSet = cfg.fontcharset;
+    lfont.lfCharSet = cfg.font.charset;
     lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
     lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
     lfont.lfQuality = DEFAULT_QUALITY;
     lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
-    strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
+    strncpy(lfont.lfFaceName, cfg.font.name, LF_FACESIZE);
 
     SelectObject(hdc, fonts[FONT_NORMAL]);
     GetTextMetrics(hdc, &tm);
@@ -1128,7 +1168,7 @@ static void init_fonts(int pick_width, int pick_height)
        ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
     }
 
-    f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
+    f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE);
 
     /*
      * Some fonts, e.g. 9-pt Courier, draw their underlines
@@ -1179,7 +1219,7 @@ static void init_fonts(int pick_width, int pick_height)
     }
 
     if (bold_mode == BOLD_FONT) {
-       f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
+       f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE);
     }
 #undef f
 
@@ -1230,7 +1270,7 @@ static void another_font(int fontno)
     if (basefont != fontno && !fontflag[basefont])
        another_font(basefont);
 
-    if (cfg.fontisbold) {
+    if (cfg.font.isbold) {
        fw_dontcare = FW_BOLD;
        fw_bold = FW_HEAVY;
     } else {
@@ -1238,10 +1278,10 @@ static void another_font(int fontno)
        fw_bold = FW_BOLD;
     }
 
-    c = cfg.fontcharset;
+    c = cfg.font.charset;
     w = fw_dontcare;
     u = FALSE;
-    s = cfg.font;
+    s = cfg.font.name;
     x = font_width;
 
     if (fontno & FONT_WIDE)
@@ -1551,7 +1591,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
 
     if (send_raw_mouse && !(cfg.mouse_override && shift)) {
        lastbtn = MBT_NOTHING;
-       term_mouse(term, b, MA_CLICK, x, y, shift, ctrl, alt);
+       term_mouse(term, b, translate_button(b), MA_CLICK,
+                  x, y, shift, ctrl, alt);
        return;
     }
 
@@ -1564,7 +1605,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
        lastact = MA_CLICK;
     }
     if (lastact != MA_NOTHING)
-       term_mouse(term, b, lastact, x, y, shift, ctrl, alt);
+       term_mouse(term, b, translate_button(b), lastact,
+                  x, y, shift, ctrl, alt);
     lasttime = thistime;
 }
 
@@ -1572,7 +1614,7 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
  * into a cooked one (SELECT, EXTEND, PASTE).
  */
-Mouse_Button translate_button(void *frontend, Mouse_Button button)
+static Mouse_Button translate_button(Mouse_Button button)
 {
     if (button == MBT_LEFT)
        return MBT_SELECT;
@@ -1653,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);
@@ -1711,7 +1757,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
                        char *session =
                            sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
-                       cl = smalloc(16 + strlen(session));
+                       cl = snewn(16 + strlen(session), char);
                                       /* 8, but play safe */
                        if (!cl)
                            cl = NULL;    
@@ -1866,11 +1912,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                                  icon_name);
                }
 
-               if (strcmp(cfg.font, prev_cfg.font) != 0 ||
+               if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||
                    strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
-                   cfg.fontisbold != prev_cfg.fontisbold ||
-                   cfg.fontheight != prev_cfg.fontheight ||
-                   cfg.fontcharset != prev_cfg.fontcharset ||
+                   cfg.font.isbold != prev_cfg.font.isbold ||
+                   cfg.font.height != prev_cfg.font.height ||
+                   cfg.font.charset != prev_cfg.font.charset ||
                    cfg.vtmode != prev_cfg.vtmode ||
                    cfg.bold_colour != prev_cfg.bold_colour ||
                    cfg.resize_action == RESIZE_DISABLED ||
@@ -1893,58 +1939,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            term_pwron(term);
            ldisc_send(ldisc, NULL, 0, 0);
            break;
-         case IDM_TEL_AYT:
-           back->special(backhandle, TS_AYT);
-           net_pending_errors();
-           break;
-         case IDM_TEL_BRK:
-           back->special(backhandle, TS_BRK);
-           net_pending_errors();
-           break;
-         case IDM_TEL_SYNCH:
-           back->special(backhandle, TS_SYNCH);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EC:
-           back->special(backhandle, TS_EC);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EL:
-           back->special(backhandle, TS_EL);
-           net_pending_errors();
-           break;
-         case IDM_TEL_GA:
-           back->special(backhandle, TS_GA);
-           net_pending_errors();
-           break;
-         case IDM_TEL_NOP:
-           back->special(backhandle, TS_NOP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_ABORT:
-           back->special(backhandle, TS_ABORT);
-           net_pending_errors();
-           break;
-         case IDM_TEL_AO:
-           back->special(backhandle, TS_AO);
-           net_pending_errors();
-           break;
-         case IDM_TEL_IP:
-           back->special(backhandle, TS_IP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_SUSP:
-           back->special(backhandle, TS_SUSP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EOR:
-           back->special(backhandle, TS_EOR);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EOF:
-           back->special(backhandle, TS_EOF);
-           net_pending_errors();
-           break;
          case IDM_ABOUT:
            showabout(hwnd);
            break;
@@ -1979,6 +1973,22 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
                SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
            }
+           if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
+               int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
+               int j;
+               /*
+                * Ensure we haven't been sent a bogus SYSCOMMAND
+                * which would cause us to reference invalid memory
+                * and crash. Perhaps I'm just too paranoid here.
+                */
+               for (j = 0; j < i; j++)
+                   if (!specials || !specials[j].name)
+                       break;
+               if (j == i) {
+                   back->special(backhandle, specials[i].code);
+                   net_pending_errors();
+               }
+           }
        }
        break;
 
@@ -2043,7 +2053,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                      is_alt_pressed());
                SetCapture(hwnd);
            } else {
-               term_mouse(term, button, MA_RELEASE,
+               term_mouse(term, button, translate_button(button), MA_RELEASE,
                           TO_CHR_X(X_POS(lParam)),
                           TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
                           wParam & MK_CONTROL, is_alt_pressed());
@@ -2068,7 +2078,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                b = MBT_MIDDLE;
            else
                b = MBT_RIGHT;
-           term_mouse(term, b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
+           term_mouse(term, b, translate_button(b), MA_DRAG,
+                      TO_CHR_X(X_POS(lParam)),
                       TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
                       wParam & MK_CONTROL, is_alt_pressed());
        }
@@ -2518,7 +2529,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
 
            if (n > 0) {
                int i;
-               buff = (char*) smalloc(n);
+               buff = snewn(n, char);
                ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
                /*
                 * Jaeyoun Chung reports that Korean character
@@ -2569,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;
@@ -2603,12 +2622,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                if (send_raw_mouse &&
                    !(cfg.mouse_override && shift_pressed)) {
                    /* send a mouse-down followed by a mouse up */
-                   term_mouse(term, b,
+                   term_mouse(term, b, translate_button(b),
                               MA_CLICK,
                               TO_CHR_X(X_POS(lParam)),
                               TO_CHR_Y(Y_POS(lParam)), shift_pressed,
                               control_pressed, is_alt_pressed());
-                   term_mouse(term, b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
+                   term_mouse(term, b, translate_button(b),
+                              MA_RELEASE, TO_CHR_X(X_POS(lParam)),
                               TO_CHR_Y(Y_POS(lParam)), shift_pressed,
                               control_pressed, is_alt_pressed());
                } else {
@@ -2705,7 +2725,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        int i;
        if (len > IpDxLEN) {
            sfree(IpDx);
-           IpDx = smalloc((len + 16) * sizeof(int));
+           IpDx = snewn(len + 16, int);
            IpDxLEN = (len + 16);
        }
        for (i = 0; i < IpDxLEN; i++)
@@ -2784,8 +2804,10 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     if ((attr & CSET_MASK) == ATTR_OEMCP)
        nfont |= FONT_OEM;
 
-    nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
-    nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
+    nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
+    nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
+    nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
+    nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
        nfont |= FONT_BOLD;
     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
@@ -2807,9 +2829,9 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        nbg = t;
     }
     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
-       nfg++;
+       nfg |= 1;
     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
-       nbg++;
+       nbg |= 1;
     fg = colours[nfg];
     bg = colours[nbg];
     SelectObject(hdc, fonts[nfont]);
@@ -2834,7 +2856,8 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        int nlen, mptr;
        if (len > uni_len) {
            sfree(uni_buf);
-           uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
+           uni_len = len;
+           uni_buf = snewn(uni_len, wchar_t);
        }
 
        for(nlen = mptr = 0; mptr<len; mptr++) {
@@ -2894,7 +2917,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        if (wlen < len) {
            sfree(wbuf);
            wlen = len;
-           wbuf = smalloc(wlen * sizeof(WCHAR));
+           wbuf = snewn(wlen, WCHAR);
        }
        for (i = 0; i < len; i++)
            wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
@@ -3508,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';
@@ -3843,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];
@@ -3904,7 +3932,7 @@ void request_paste(void *frontend)
 void set_title(void *frontend, char *title)
 {
     sfree(window_name);
-    window_name = smalloc(1 + strlen(title));
+    window_name = snewn(1 + strlen(title), char);
     strcpy(window_name, title);
     if (cfg.win_name_always || !IsIconic(hwnd))
        SetWindowText(hwnd, title);
@@ -3913,7 +3941,7 @@ void set_title(void *frontend, char *title)
 void set_icon(void *frontend, char *title)
 {
     sfree(icon_name);
-    icon_name = smalloc(1 + strlen(title));
+    icon_name = snewn(1 + strlen(title), char);
     strcpy(icon_name, title);
     if (!cfg.win_name_always && IsIconic(hwnd))
        SetWindowText(hwnd, title);
@@ -4083,10 +4111,10 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
 
        get_unitab(CP_ACP, unitab, 0);
 
-       rtfsize = 100 + strlen(cfg.font);
-       rtf = smalloc(rtfsize);
+       rtfsize = 100 + strlen(cfg.font.name);
+       rtf = snewn(rtfsize, char);
        sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
-               GetACP(), cfg.font);
+               GetACP(), cfg.font.name);
        rtflen = strlen(rtf);
 
        /*
@@ -4149,7 +4177,7 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
 
            if (rtfsize < rtflen + totallen + 3) {
                rtfsize = rtflen + totallen + 512;
-               rtf = srealloc(rtf, rtfsize);
+               rtf = sresize(rtf, rtfsize, char);
            }
 
            strcpy(rtf + rtflen, before); rtflen += blen;
@@ -4237,7 +4265,7 @@ void get_clip(void *frontend, wchar_t ** p, int *len)
            CloseClipboard();
            s = GlobalLock(clipdata);
            i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
-           *p = converted = smalloc(i * sizeof(wchar_t));
+           *p = converted = snewn(i, wchar_t);
            MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
            *len = i - 1;
            return;
@@ -4276,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);
 }
 
@@ -4291,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);
 }
 
@@ -4362,14 +4394,34 @@ void beep(void *frontend, int mode)
         */
        lastbeep = GetTickCount();
     } else if (mode == BELL_WAVEFILE) {
-       if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
+       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) {
@@ -4606,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);
+}