Colin's const-fixing Patch Of Death. Seems to build fine on Windows
[u/mdw/putty] / window.c
index abbfdd8..c9d8df5 100644 (file)
--- a/window.c
+++ b/window.c
 #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
@@ -78,6 +70,7 @@
 #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);
@@ -87,7 +80,6 @@ static void init_fonts(int, int);
 static void another_font(int);
 static void deinit_fonts(void);
 static void set_input_locale(HKL);
-static int do_mouse_wheel_msg(UINT message, WPARAM wParam, LPARAM lParam);
 
 static int is_full_screen(void);
 static void make_full_screen(void);
@@ -108,12 +100,36 @@ static LPARAM pend_netevent_lParam = 0;
 static void enact_pending_netevent(void);
 static void flash_window(int mode);
 static void sys_cursor_update(void);
+static int is_shift_pressed(void);
 static int get_fullscreen_rect(RECT * ss);
 
 static time_t last_movement = 0;
 
 static int caret_x = -1, caret_y = -1;
 
+static int kbd_codepage;
+
+static void *ldisc;
+static Backend *back;
+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
@@ -168,13 +184,12 @@ static OSVERSIONINFO osVersion;
 static UINT wm_mousewheel = WM_MOUSEWHEEL;
 
 /* Dummy routine, only required in plink. */
-void ldisc_update(int echo, int edit)
+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;
@@ -210,8 +225,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        ZeroMemory(&osVersion, sizeof(osVersion));
        osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
        if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
+           char *str = dupprintf("%s Fatal Error", appname);
             MessageBox(NULL, "Windows refuses to report a version",
-                       "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
+                       str, MB_OK | MB_ICONEXCLAMATION);
+           sfree(str);
            return 1;
         }
     }
@@ -259,8 +276,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);
@@ -319,7 +345,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                char *p = argv[i];
                int ret;
 
-               ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL, 1);
+               ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
+                                           1, &cfg);
                if (ret == -2) {
                    cmdline_error("option \"%s\" requires an argument", p);
                } else if (ret == 2) {
@@ -332,18 +359,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;
@@ -355,7 +385,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                         * argument, so that it will be deferred
                         * until it's a good moment to run it.
                         */
-                       int ret = cmdline_process_param("-P", p, 1);
+                       int ret = cmdline_process_param("-P", p, 1, &cfg);
                        assert(ret == 2);
                    } else if (!strncmp(q, "telnet:", 7)) {
                        /*
@@ -395,11 +425,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                        cfg.host[sizeof(cfg.host) - 1] = '\0';
                        got_host = 1;
                    }
+               } else {
+                   cmdline_error("unknown option \"%s\"", p);
                }
            }
        }
 
-       cmdline_run_saved();
+       cmdline_run_saved(&cfg);
 
        if (!*cfg.host && !do_config()) {
            WSACleanup();
@@ -461,8 +493,10 @@ 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);
+                      str, MB_OK | MB_ICONEXCLAMATION);
+           sfree(str);
            WSACleanup();
            return 1;
        }
@@ -470,8 +504,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     /* 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);
+                  str, MB_OK | MB_ICONEXCLAMATION);
+       sfree(str);
        WSACleanup();
        return 1;
     }
@@ -493,7 +529,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     hwnd = NULL;
 
-    term = term_init();
+    memset(&ucsdata, 0, sizeof(ucsdata));
+
+    term = term_init(&cfg, &ucsdata, NULL);
+    logctx = log_init(NULL, &cfg);
+    term_provide_logctx(term, logctx);
 
     cfgtopalette();
 
@@ -570,7 +610,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);
@@ -596,30 +636,43 @@ 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,
+       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);
-       set_title(title);
-       set_icon(title);
+       set_title(NULL, title);
+       set_icon(NULL, title);
     }
 
+    /*
+     * Connect the terminal to the backend for resize purposes.
+     */
+    term_provide_resize_fn(term, back->size, backhandle);
+
+    /*
+     * Set up a line discipline.
+     */
+    ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
+
     session_closed = FALSE;
 
     /*
@@ -634,41 +687,23 @@ 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...");
        AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
        s = CreateMenu();
-       get_sesslist(TRUE);
-       for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
+       get_sesslist(&sesslist, TRUE);
+       for (i = 1;
+            i < ((sesslist.nsessions < 256) ? sesslist.nsessions : 256);
+            i++)
            AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
-                      sessions[i]);
+                      sesslist.sessions[i]);
        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);
@@ -681,9 +716,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.
      */
@@ -692,7 +731,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     /*
      * Open the initial log file if there is one.
      */
-    logfopen();
+    logfopen(logctx);
 
     /*
      * Finally show the window!
@@ -745,9 +784,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;
            }
@@ -848,9 +884,40 @@ 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(int activate)
+void set_raw_mouse_mode(void *frontend, int activate)
 {
     activate = activate && !cfg.no_mouse_rep;
     send_raw_mouse = activate;
@@ -860,20 +927,25 @@ void set_raw_mouse_mode(int activate)
 /*
  * Print a message box and close the connection.
  */
-void connection_fatal(char *fmt, ...)
+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;
-       SetWindowText(hwnd, "PuTTY (inactive)");
+       sprintf(morestuff, "%.70s (inactive)", appname);
+       set_icon(NULL, morestuff);
+       set_title(NULL, morestuff);
     }
 }
 
@@ -883,12 +955,14 @@ void connection_fatal(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);
 }
 
@@ -913,13 +987,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;
-           SetWindowText(hwnd, "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);
        }
     }
 }
@@ -954,6 +1031,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));
@@ -1017,7 +1098,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 {
@@ -1030,7 +1111,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);
@@ -1042,9 +1123,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;
@@ -1054,12 +1135,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);
@@ -1082,19 +1163,18 @@ static void init_fonts(int pick_width, int pick_height)
 
        /* !!! Yes the next line is right */
        if (cset == OEM_CHARSET)
-           font_codepage = GetOEMCP();
+           ucsdata.font_codepage = GetOEMCP();
        else
-           if (TranslateCharsetInfo
-               ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
-               info.ciACP;
+           if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET))
+               ucsdata.font_codepage = info.ciACP;
        else
-           font_codepage = -1;
+           ucsdata.font_codepage = -1;
 
-       GetCPInfo(font_codepage, &cpinfo);
-       dbcs_screenfont = (cpinfo.MaxCharSize > 1);
+       GetCPInfo(ucsdata.font_codepage, &cpinfo);
+       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
@@ -1145,7 +1225,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
 
@@ -1179,7 +1259,7 @@ static void init_fonts(int pick_width, int pick_height)
     }
     fontflag[0] = fontflag[1] = fontflag[2] = 1;
 
-    init_ucs_tables();
+    init_ucs(&cfg, &ucsdata);
 }
 
 static void another_font(int fontno)
@@ -1196,7 +1276,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 {
@@ -1204,10 +1284,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)
@@ -1241,7 +1321,7 @@ static void deinit_fonts(void)
     }
 }
 
-void request_resize(int w, int h)
+void request_resize(void *frontend, int w, int h)
 {
     int width, height;
 
@@ -1517,7 +1597,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;
     }
 
@@ -1530,7 +1611,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;
 }
 
@@ -1538,7 +1620,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(Mouse_Button button)
+static Mouse_Button translate_button(Mouse_Button button)
 {
     if (button == MBT_LEFT)
        return MBT_SELECT;
@@ -1610,7 +1692,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            time_t now;
            time(&now);
            if (now - last_movement > cfg.ping_interval) {
-               back->special(TS_PING);
+               back->special(backhandle, TS_PING);
                last_movement = now;
            }
        }
@@ -1619,13 +1701,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);
@@ -1674,10 +1760,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    sprintf(c, "putty &%p", filemap);
                    cl = c;
                } else if (wParam == IDM_SAVEDSESS) {
-                   if ((lParam - IDM_SAVED_MIN) / 16 < nsessions) {
+                   if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
                        char *session =
-                           sessions[(lParam - IDM_SAVED_MIN) / 16];
-                       cl = smalloc(16 + strlen(session));
+                           sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
+                       cl = snewn(16 + strlen(session), char);
                                       /* 8, but play safe */
                        if (!cl)
                            cl = NULL;    
@@ -1732,18 +1818,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    }
                }
 
-               if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
-                   prev_cfg.logtype != cfg.logtype) {
-                   logfclose();       /* reset logging */
-                   logfopen();
-               }
+               /* Pass new config data to the logging module */
+               log_reconfig(logctx, &cfg);
 
                sfree(logpal);
                /*
                 * Flush the line discipline's edit buffer in the
                 * case where local editing has just been disabled.
                 */
-               ldisc_send(NULL, 0, 0);
+               ldisc_send(ldisc, NULL, 0, 0);
                if (pal)
                    DeleteObject(pal);
                logpal = NULL;
@@ -1751,8 +1834,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                cfgtopalette();
                init_palette();
 
-               /* Give terminal a heads-up on miscellaneous stuff */
-               term_reconfig(term);
+               /* Pass new config data to the terminal */
+               term_reconfig(term, &cfg);
+
+               /* Pass new config data to the back end */
+               back->reconfig(backhandle, &cfg);
 
                /* Screen size changed ? */
                if (cfg.height != prev_cfg.height ||
@@ -1825,18 +1911,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    init_lvl = 2;
                }
 
-               set_title(cfg.wintitle);
+               set_title(NULL, cfg.wintitle);
                if (IsIconic(hwnd)) {
                    SetWindowText(hwnd,
                                  cfg.win_name_always ? window_name :
                                  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 ||
@@ -1857,59 +1943,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            break;
          case IDM_RESET:
            term_pwron(term);
-           ldisc_send(NULL, 0, 0);
-           break;
-         case IDM_TEL_AYT:
-           back->special(TS_AYT);
-           net_pending_errors();
-           break;
-         case IDM_TEL_BRK:
-           back->special(TS_BRK);
-           net_pending_errors();
-           break;
-         case IDM_TEL_SYNCH:
-           back->special(TS_SYNCH);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EC:
-           back->special(TS_EC);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EL:
-           back->special(TS_EL);
-           net_pending_errors();
-           break;
-         case IDM_TEL_GA:
-           back->special(TS_GA);
-           net_pending_errors();
-           break;
-         case IDM_TEL_NOP:
-           back->special(TS_NOP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_ABORT:
-           back->special(TS_ABORT);
-           net_pending_errors();
-           break;
-         case IDM_TEL_AO:
-           back->special(TS_AO);
-           net_pending_errors();
-           break;
-         case IDM_TEL_IP:
-           back->special(TS_IP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_SUSP:
-           back->special(TS_SUSP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EOR:
-           back->special(TS_EOR);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EOF:
-           back->special(TS_EOF);
-           net_pending_errors();
+           ldisc_send(ldisc, NULL, 0, 0);
            break;
          case IDM_ABOUT:
            showabout(hwnd);
@@ -1945,6 +1979,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;
 
@@ -2009,7 +2059,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());
@@ -2034,7 +2084,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());
        }
@@ -2064,7 +2115,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                       (p.rcPaint.left-offset_width)/font_width,
                       (p.rcPaint.top-offset_height)/font_height,
                       (p.rcPaint.right-offset_width-1)/font_width,
-                      (p.rcPaint.bottom-offset_height-1)/font_height);
+                      (p.rcPaint.bottom-offset_height-1)/font_height,
+                      is_alt_pressed());
 
            if (p.fErase ||
                p.rcPaint.left  < offset_width  ||
@@ -2171,7 +2223,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
         * 1) Keep the sizetip uptodate
         * 2) Make sure the window size is _stepped_ in units of the font size.
         */
-       if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
+       if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
            int width, height, w, h, ew, eh;
            LPRECT r = (LPRECT) lParam;
 
@@ -2332,7 +2384,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
             * down the connection during an NT opaque drag.)
             */
            if (resizing) {
-               if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
+               if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
                    need_backend_resize = TRUE;
                    w = (width-cfg.window_border*2) / font_width;
                    if (w < 1) w = 1;
@@ -2375,7 +2427,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        break;
       case WM_PALETTECHANGED:
        if ((HWND) wParam != hwnd && pal != NULL) {
-           HDC hdc = get_ctx();
+           HDC hdc = get_ctx(NULL);
            if (hdc) {
                if (RealizePalette(hdc) > 0)
                    UpdateColors(hdc);
@@ -2385,7 +2437,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        break;
       case WM_QUERYNEWPALETTE:
        if (pal != NULL) {
-           HDC hdc = get_ctx();
+           HDC hdc = get_ctx(NULL);
            if (hdc) {
                if (RealizePalette(hdc) > 0)
                    UpdateColors(hdc);
@@ -2445,7 +2497,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                     * we're sent.
                     */
                    term_seen_key_event(term);
-                   ldisc_send(buf, len, 1);
+                   ldisc_send(ldisc, buf, len, 1);
                    show_mouseptr(0);
                }
            }
@@ -2483,7 +2535,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
@@ -2493,7 +2545,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 */
                term_seen_key_event(term);
                for (i = 0; i < n; i += 2) {
-                   luni_send((unsigned short *)(buff+i), 1, 1);
+                   luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
                }
                free(buff);
            }
@@ -2508,11 +2560,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            buf[1] = wParam;
            buf[0] = wParam >> 8;
            term_seen_key_event(term);
-           lpage_send(kbd_codepage, buf, 2, 1);
+           lpage_send(ldisc, kbd_codepage, buf, 2, 1);
        } else {
            char c = (unsigned char) wParam;
            term_seen_key_event(term);
-           lpage_send(kbd_codepage, &c, 1, 1);
+           lpage_send(ldisc, kbd_codepage, &c, 1, 1);
        }
        return (0);
       case WM_CHAR:
@@ -2526,7 +2578,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        {
            char c = (unsigned char)wParam;
            term_seen_key_event(term);
-           lpage_send(CP_ACP, &c, 1, 1);
+           lpage_send(ldisc, CP_ACP, &c, 1, 1);
        }
        return 0;
       case WM_SETCURSOR:
@@ -2534,9 +2586,17 @@ 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, alt_pressed=0;
+           int shift_pressed=0, control_pressed=0;
 
            if (message == WM_MOUSEWHEEL) {
                wheel_accumulator += (short)HIWORD(wParam);
@@ -2568,12 +2628,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 {
@@ -2596,7 +2657,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
  * helper software tracks the system caret, so we should arrange to
  * have one.)
  */
-void sys_cursor(int x, int y)
+void sys_cursor(void *frontend, int x, int y)
 {
     int cx, cy;
 
@@ -2670,7 +2731,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++)
@@ -2729,8 +2790,8 @@ void do_text(Context ctx, int x, int y, char *text, int len,
            if (lattr == LATTR_TOP || lattr == LATTR_BOT)
                text_adjust *= 2;
            attr &= ~CSET_MASK;
-           text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
-           attr |= (unitab_xterm['q'] & CSET_MASK);
+           text[0] = (char) (ucsdata.unitab_xterm['q'] & CHAR_MASK);
+           attr |= (ucsdata.unitab_xterm['q'] & CSET_MASK);
            if (attr & ATTR_UNDER) {
                attr &= ~ATTR_UNDER;
                force_manual_underline = 1;
@@ -2749,8 +2810,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))
@@ -2772,9 +2835,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]);
@@ -2791,7 +2854,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        line_box.right = font_width*term->cols+offset_width;
 
     /* We're using a private area for direct to font. (512 chars.) */
-    if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
+    if (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
        /* Ho Hum, dbcs fonts are a PITA! */
        /* To display on W9x I have to convert to UCS */
        static wchar_t *uni_buf = 0;
@@ -2799,20 +2862,21 @@ 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++) {
            uni_buf[nlen] = 0xFFFD;
-           if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
+           if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
                IpDx[nlen] += char_width;
-               MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
+               MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
                                   text+mptr, 2, uni_buf+nlen, 1);
                mptr++;
            }
            else
            {
-               MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
+               MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
                                   text+mptr, 1, uni_buf+nlen, 1);
            }
            nlen++;
@@ -2859,7 +2923,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));
@@ -2970,7 +3034,7 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
 
 /* This function gets the actual width of a character in the normal font.
  */
-int CharWidth(Context ctx, int uc) {
+int char_width(Context ctx, int uc) {
     HDC hdc = ctx;
     int ibuf = 0;
 
@@ -2981,17 +3045,17 @@ int CharWidth(Context ctx, int uc) {
 
     switch (uc & CSET_MASK) {
       case ATTR_ASCII:
-       uc = unitab_line[uc & 0xFF];
+       uc = ucsdata.unitab_line[uc & 0xFF];
        break;
       case ATTR_LINEDRW:
-       uc = unitab_xterm[uc & 0xFF];
+       uc = ucsdata.unitab_xterm[uc & 0xFF];
        break;
       case ATTR_SCOACS:
-       uc = unitab_scoacs[uc & 0xFF];
+       uc = ucsdata.unitab_scoacs[uc & 0xFF];
        break;
     }
     if (DIRECT_FONT(uc)) {
-       if (dbcs_screenfont) return 1;
+       if (ucsdata.dbcs_screenfont) return 1;
 
        /* Speedup, I know of no font where ascii is the wrong width */
        if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
@@ -3170,8 +3234,6 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        }
     }
 
-    alt_pressed = (left_alt && key_down);
-
     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
        + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
@@ -3261,10 +3323,18 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
            return 0;
        }
+       if (wParam == VK_PRIOR && shift_state == 2) {
+           SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
+           return 0;
+       }
        if (wParam == VK_NEXT && shift_state == 1) {
            SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
            return 0;
        }
+       if (wParam == VK_NEXT && shift_state == 2) {
+           SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
+           return 0;
+       }
        if (wParam == VK_INSERT && shift_state == 1) {
            term_do_paste(term);
            return 0;
@@ -3459,7 +3529,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
            return p - output;
        }
-       if (shift_state == 2 && wParam == 0xBD) {
+       if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
            *p++ = 0x1F;
            return p - output;
        }
@@ -3730,7 +3800,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 #ifdef SHOW_TOASCII_RESULT
        if (r == 1 && !key_down) {
            if (alt_sum) {
-               if (in_utf(term) || dbcs_screenfont)
+               if (in_utf(term) || ucsdata.dbcs_screenfont)
                    debug((", (U+%04x)", alt_sum));
                else
                    debug((", LCH(%d)", alt_sum));
@@ -3775,7 +3845,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    }
                    keybuf = nc;
                    term_seen_key_event(term);
-                   luni_send(&keybuf, 1, 1);
+                   luni_send(ldisc, &keybuf, 1, 1);
                    continue;
                }
 
@@ -3783,10 +3853,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
                if (!key_down) {
                    if (alt_sum) {
-                       if (in_utf(term) || dbcs_screenfont) {
+                       if (in_utf(term) || ucsdata.dbcs_screenfont) {
                            keybuf = alt_sum;
                            term_seen_key_event(term);
-                           luni_send(&keybuf, 1, 1);
+                           luni_send(ldisc, &keybuf, 1, 1);
                        } else {
                            ch = (char) alt_sum;
                            /*
@@ -3799,25 +3869,26 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                             * everything we're sent.
                             */
                            term_seen_key_event(term);
-                           ldisc_send(&ch, 1, 1);
+                           ldisc_send(ldisc, &ch, 1, 1);
                        }
                        alt_sum = 0;
                    } else
                        term_seen_key_event(term);
-                       lpage_send(kbd_codepage, &ch, 1, 1);
+                       lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
                } else {
                    if(capsOn && ch < 0x80) {
                        WCHAR cbuf[2];
                        cbuf[0] = 27;
                        cbuf[1] = xlat_uskbd2cyrllic(ch);
                        term_seen_key_event(term);
-                       luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
+                       luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
                    } else {
                        char cbuf[2];
                        cbuf[0] = '\033';
                        cbuf[1] = ch;
                        term_seen_key_event(term);
-                       lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
+                       lpage_send(ldisc, kbd_codepage,
+                                  cbuf+!left_alt, 1+!!left_alt, 1);
                    }
                }
                show_mouseptr(0);
@@ -3832,7 +3903,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        if (!left_alt)
            keys[0] = 0;
        /* If we will be using alt_sum fix the 256s */
-       else if (keys[0] && (in_utf(term) || dbcs_screenfont))
+       else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
            keys[0] = 10;
     }
 
@@ -3849,7 +3920,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
     return -1;
 }
 
-void request_paste(void)
+void request_paste(void *frontend)
 {
     /*
      * In Windows, pasting is synchronous: we can read the
@@ -3859,25 +3930,25 @@ void request_paste(void)
     term_do_paste(term);
 }
 
-void set_title(char *title)
+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);
 }
 
-void set_icon(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);
 }
 
-void set_sbar(int total, int start, int page)
+void set_sbar(void *frontend, int total, int start, int page)
 {
     SCROLLINFO si;
 
@@ -3894,7 +3965,7 @@ void set_sbar(int total, int start, int page)
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
 }
 
-Context get_ctx(void)
+Context get_ctx(void *frontend)
 {
     HDC hdc;
     if (hwnd) {
@@ -3925,7 +3996,7 @@ static void real_palette_set(int n, int r, int g, int b)
        colours[n] = RGB(r, g, b);
 }
 
-void palette_set(int n, int r, int g, int b)
+void palette_set(void *frontend, int n, int r, int g, int b)
 {
     static const int first[21] = {
        0, 2, 4, 6, 8, 10, 12, 14,
@@ -3936,14 +4007,14 @@ void palette_set(int n, int r, int g, int b)
     if (first[n] >= 18)
        real_palette_set(first[n] + 1, r, g, b);
     if (pal) {
-       HDC hdc = get_ctx();
+       HDC hdc = get_ctx(frontend);
        UnrealizeObject(pal);
        RealizePalette(hdc);
        free_ctx(hdc);
     }
 }
 
-void palette_reset(void)
+void palette_reset(void *frontend)
 {
     int i;
 
@@ -3964,13 +4035,13 @@ void palette_reset(void)
     if (pal) {
        HDC hdc;
        SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
-       hdc = get_ctx();
+       hdc = get_ctx(frontend);
        RealizePalette(hdc);
        free_ctx(hdc);
     }
 }
 
-void write_aclip(char *data, int len, int must_deselect)
+void write_aclip(void *frontend, char *data, int len, int must_deselect)
 {
     HGLOBAL clipdata;
     void *lock;
@@ -4002,7 +4073,7 @@ void write_aclip(char *data, int len, int must_deselect)
 /*
  * Note: unlike write_aclip() this will not append a nul.
  */
-void write_clip(wchar_t * data, int len, int must_deselect)
+void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
 {
     HGLOBAL clipdata, clipdata2, clipdata3;
     int len2;
@@ -4041,10 +4112,10 @@ void write_clip(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);
 
        /*
@@ -4107,7 +4178,7 @@ void write_clip(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;
@@ -4165,7 +4236,7 @@ void write_clip(wchar_t * data, int len, int must_deselect)
        SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
 }
 
-void get_clip(wchar_t ** p, int *len)
+void get_clip(void *frontend, wchar_t ** p, int *len)
 {
     static HGLOBAL clipdata = NULL;
     static wchar_t *converted = 0;
@@ -4195,7 +4266,7 @@ void get_clip(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;
@@ -4212,7 +4283,7 @@ void get_clip(wchar_t ** p, int *len)
  * Move `lines' lines from position `from' to position `to' in the
  * window.
  */
-void optimised_move(int to, int from, int lines)
+void optimised_move(void *frontend, int to, int from, int lines)
 {
     RECT r;
     int min, max;
@@ -4234,12 +4305,14 @@ void optimised_move(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);
 }
 
@@ -4249,13 +4322,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);
 }
 
@@ -4298,7 +4373,7 @@ static void flash_window(int mode)
 /*
  * Beep.
  */
-void beep(int mode)
+void beep(void *frontend, int mode)
 {
     if (mode == BELL_DEFAULT) {
        /*
@@ -4320,11 +4395,14 @@ void beep(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;
        }
@@ -4339,7 +4417,7 @@ void beep(int mode)
  * Minimise or restore the window in response to a server-side
  * request.
  */
-void set_iconic(int iconic)
+void set_iconic(void *frontend, int iconic)
 {
     if (IsIconic(hwnd)) {
        if (!iconic)
@@ -4353,7 +4431,7 @@ void set_iconic(int iconic)
 /*
  * Move the window in response to a server-side request.
  */
-void move_window(int x, int y)
+void move_window(void *frontend, int x, int y)
 {
     if (cfg.resize_action == RESIZE_DISABLED || 
         cfg.resize_action == RESIZE_FONT ||
@@ -4367,7 +4445,7 @@ void move_window(int x, int y)
  * Move the window to the top or bottom of the z-order in response
  * to a server-side request.
  */
-void set_zorder(int top)
+void set_zorder(void *frontend, int top)
 {
     if (cfg.alwaysontop)
        return;                        /* ignore */
@@ -4378,7 +4456,7 @@ void set_zorder(int top)
 /*
  * Refresh the window in response to a server-side request.
  */
-void refresh_window(void)
+void refresh_window(void *frontend)
 {
     InvalidateRect(hwnd, NULL, TRUE);
 }
@@ -4387,7 +4465,7 @@ void refresh_window(void)
  * Maximise or restore the window in response to a server-side
  * request.
  */
-void set_zoomed(int zoomed)
+void set_zoomed(void *frontend, int zoomed)
 {
     if (IsZoomed(hwnd)) {
         if (!zoomed)
@@ -4401,7 +4479,7 @@ void set_zoomed(int zoomed)
 /*
  * Report whether the window is iconic, for terminal reports.
  */
-int is_iconic(void)
+int is_iconic(void *frontend)
 {
     return IsIconic(hwnd);
 }
@@ -4409,7 +4487,7 @@ int is_iconic(void)
 /*
  * Report the window's position, for terminal reports.
  */
-void get_window_pos(int *x, int *y)
+void get_window_pos(void *frontend, int *x, int *y)
 {
     RECT r;
     GetWindowRect(hwnd, &r);
@@ -4420,7 +4498,7 @@ void get_window_pos(int *x, int *y)
 /*
  * Report the window's pixel size, for terminal reports.
  */
-void get_window_pixels(int *x, int *y)
+void get_window_pixels(void *frontend, int *x, int *y)
 {
     RECT r;
     GetWindowRect(hwnd, &r);
@@ -4431,7 +4509,7 @@ void get_window_pixels(int *x, int *y)
 /*
  * Return the window or icon title.
  */
-char *get_window_title(int icon)
+char *get_window_title(void *frontend, int icon)
 {
     return icon ? icon_name : window_name;
 }
@@ -4553,3 +4631,30 @@ void flip_full_screen()
        ShowWindow(hwnd, SW_MAXIMIZE);
     }
 }
+
+void frontend_keypress(void *handle)
+{
+    /*
+     * Keypress termination in non-Close-On-Exit mode is not
+     * currently supported in PuTTY proper, because the window
+     * always has a perfectly good Close button anyway. So we do
+     * nothing here.
+     */
+    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);
+}