Re-engineering of terminal emulator, phase 1.
[u/mdw/putty] / window.c
index 1c3e225..5a36973 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_RESTART   0x0040
+#define IDM_RECONF    0x0050
+#define IDM_CLRSB     0x0060
+#define IDM_RESET     0x0070
 #define IDM_HELP      0x0140
 #define IDM_ABOUT     0x0150
 #define IDM_SAVEDSESS 0x0160
 #define IDM_COPYALL   0x0170
 #define IDM_FULLSCREEN 0x0180
+#define IDM_PASTE     0x0190
 
 #define IDM_SESSLGP   0x0250          /* log type printable */
 #define IDM_SESSLGA   0x0260          /* log type all chars */
@@ -75,6 +68,7 @@ static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        unsigned char *output);
 static void cfgtopalette(void);
+static void systopalette(void);
 static void init_palette(void);
 static void init_fonts(int, int);
 static void another_font(int);
@@ -117,7 +111,12 @@ static struct unicode_data ucsdata;
 static int session_closed;
 
 static const struct telnet_special *specials;
-static int specials_menu_position;
+
+static struct {
+    HMENU menu;
+    int specials_submenu_pos;
+} popup_menus[2];
+enum { SYSMENU, CTXMENU };
 
 Config cfg;                           /* exported to windlg.c */
 
@@ -177,10 +176,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. */
@@ -188,10 +183,112 @@ void ldisc_update(void *frontend, int echo, int edit)
 {
 }
 
+static void start_backend(void)
+{
+    const char *error;
+    char msg[1024], *title;
+    char *realhost;
+    int i;
+
+    /*
+     * Select protocol. This is farmed out into a table in a
+     * separate file to enable an ssh-free variant.
+     */
+    back = NULL;
+    for (i = 0; backends[i].backend != NULL; i++)
+       if (backends[i].protocol == cfg.protocol) {
+           back = backends[i].backend;
+           break;
+       }
+    if (back == NULL) {
+       char *str = dupprintf("%s Internal Error", appname);
+       MessageBox(NULL, "Unsupported protocol number found",
+                  str, MB_OK | MB_ICONEXCLAMATION);
+       sfree(str);
+       cleanup_exit(1);
+    }
+
+    error = back->init(NULL, &backhandle, &cfg,
+                      cfg.host, cfg.port, &realhost, cfg.tcp_nodelay,
+                      cfg.tcp_keepalives);
+    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, str, MB_ICONERROR | MB_OK);
+       sfree(str);
+       exit(0);
+    }
+    window_name = icon_name = NULL;
+    if (*cfg.wintitle) {
+       title = cfg.wintitle;
+    } else {
+       sprintf(msg, "%s - %s", realhost, appname);
+       title = msg;
+    }
+    sfree(realhost);
+    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);
+
+    /*
+     * Destroy the Restart Session menu item. (This will return
+     * failure if it's already absent, as it will be the very first
+     * time we call this function. We ignore that, because as long
+     * as the menu item ends up not being there, we don't care
+     * whether it was us who removed it or not!)
+     */
+    for (i = 0; i < lenof(popup_menus); i++) {
+       DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
+    }
+
+    session_closed = FALSE;
+}
+
+static void close_session(void)
+{
+    char morestuff[100];
+    int i;
+
+    session_closed = TRUE;
+    sprintf(morestuff, "%.70s (inactive)", appname);
+    set_icon(NULL, morestuff);
+    set_title(NULL, morestuff);
+
+    if (ldisc) {
+       ldisc_free(ldisc);
+       ldisc = NULL;
+    }
+    if (back) {
+       back->free(backhandle);
+       backhandle = NULL;
+       back = NULL;
+       update_specials_menu(NULL);
+    }
+
+    /*
+     * Show the Restart Session menu item. Do a precautionary
+     * delete first to ensure we never end up with more than one.
+     */
+    for (i = 0; i < lenof(popup_menus); i++) {
+       DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
+       InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED,
+                  IDM_RESTART, "&Restart Session");
+    }
+}
+
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
-    WORD winsock_ver;
-    WSADATA wsadata;
     WNDCLASS wndclass;
     MSG msg;
     int guess_width, guess_height;
@@ -199,20 +296,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();
@@ -221,16 +304,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)) {
-           char *str = dupprintf("%s Fatal Error", appname);
-            MessageBox(NULL, "Windows refuses to report a version",
-                       str, MB_OK | MB_ICONEXCLAMATION);
-           sfree(str);
-           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;
     }
 
     /*
@@ -309,8 +389,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 == '&') {
            /*
@@ -328,8 +407,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 {
            /*
@@ -434,8 +512,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);
        }
 
        /*
@@ -448,7 +525,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
        /* See if host is of the form user@host */
        if (cfg.host[0] != '\0') {
-           char *atsign = strchr(cfg.host, '@');
+           char *atsign = strrchr(cfg.host, '@');
            /* Make sure we're not overflowing the user field */
            if (atsign) {
                if (atsign - cfg.host < sizeof cfg.username) {
@@ -480,36 +557,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        }
     }
 
-    /*
-     * Select protocol. This is farmed out into a table in a
-     * separate file to enable an ssh-free variant.
-     */
-    {
-       int i;
-       back = NULL;
-       for (i = 0; backends[i].backend != NULL; i++)
-           if (backends[i].protocol == cfg.protocol) {
-               back = backends[i].backend;
-               break;
-           }
-       if (back == NULL) {
-           char *str = dupprintf("%s Internal Error", appname);
-           MessageBox(NULL, "Unsupported protocol number found",
-                      str, MB_OK | MB_ICONEXCLAMATION);
-           sfree(str);
-           WSACleanup();
-           return 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",
                   str, MB_OK | MB_ICONEXCLAMATION);
        sfree(str);
-       WSACleanup();
-       return 1;
+       cleanup_exit(1);
     }
 
     if (!prev) {
@@ -632,48 +686,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
     }
 
-    /*
-     * Start up the telnet connection.
-     */
-    {
-       const char *error;
-       char msg[1024], *title;
-       char *realhost;
-
-       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, str, MB_ICONERROR | MB_OK);
-           sfree(str);
-           return 0;
-       }
-       window_name = icon_name = NULL;
-       if (*cfg.wintitle) {
-           title = cfg.wintitle;
-       } else {
-           sprintf(msg, "%s - %s", realhost, appname);
-           title = msg;
-       }
-       sfree(realhost);
-       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;
+    start_backend();
 
     /*
      * Prepare the mouse handler.
@@ -686,17 +699,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      * Set up the session-control options on the system menu.
      */
     {
-       HMENU m = GetSystemMenu(hwnd, FALSE);
-       HMENU s;
-       int i;
+       HMENU s, m;
+       int i, j;
        char *str;
 
-       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");
+       popup_menus[SYSMENU].menu = GetSystemMenu(hwnd, FALSE);
+       popup_menus[CTXMENU].menu = CreatePopupMenu();
+       AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_PASTE, "&Paste");
+
        s = CreateMenu();
        get_sesslist(&sesslist, TRUE);
        for (i = 1;
@@ -704,21 +714,32 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
             i++)
            AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * 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);
-       AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
-       AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
-       AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
-       AppendMenu(m, MF_SEPARATOR, 0, 0);
-       AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
-                  MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
-       AppendMenu(m, MF_SEPARATOR, 0, 0);
-        if (help_path)
-            AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
-       str = dupprintf("&About %s", appname);
-       AppendMenu(m, MF_ENABLED, IDM_ABOUT, str);
-       sfree(str);
+
+       for (j = 0; j < lenof(popup_menus); j++) {
+           m = popup_menus[j].menu;
+
+           AppendMenu(m, MF_SEPARATOR, 0, 0);
+           popup_menus[j].specials_submenu_pos = 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");
+           AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
+           AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
+           AppendMenu(m, MF_SEPARATOR, 0, 0);
+           AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
+           AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
+           AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
+           AppendMenu(m, MF_SEPARATOR, 0, 0);
+           AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
+                      MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
+           AppendMenu(m, MF_SEPARATOR, 0, 0);
+           if (help_path)
+               AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
+           str = dupprintf("&About %s", appname);
+           AppendMenu(m, MF_ENABLED, IDM_ABOUT, str);
+           sfree(str);
+       }
     }
 
     update_specials_menu(NULL);
@@ -844,8 +865,6 @@ void cleanup_exit(int code)
     if (pal)
        DeleteObject(pal);
     sk_cleanup();
-    if (wsa_started)
-       WSACleanup();
 
     if (cfg.protocol == PROT_SSH) {
        random_save_seed();
@@ -872,8 +891,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:
@@ -888,13 +907,17 @@ char *do_select(SOCKET skt, int startup)
  */
 void update_specials_menu(void *frontend)
 {
-    HMENU m = GetSystemMenu(hwnd, FALSE);
+    HMENU p;
     int menu_already_exists = (specials != NULL);
-    int i;
+    int i, j;
+
+    if (back)
+       specials = back->get_specials(backhandle);
+    else
+       specials = NULL;
 
-    specials = back->get_specials(backhandle);
     if (specials) {
-       HMENU p = CreateMenu();
+       p = CreateMenu();
        for (i = 0; specials[i].name; i++) {
            assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
            if (*specials[i].name)
@@ -903,14 +926,27 @@ void update_specials_menu(void *frontend)
            else
                AppendMenu(p, MF_SEPARATOR, 0, 0);
        }
-       if (menu_already_exists)
-           DeleteMenu(m, specials_menu_position, MF_BYPOSITION);
-       else
-           InsertMenu(m, specials_menu_position,
+    } else
+       p = NULL;
+
+    for (j = 0; j < lenof(popup_menus); j++) {
+       if (menu_already_exists) {
+           DeleteMenu(popup_menus[j].menu,
+                      popup_menus[j].specials_submenu_pos,
+                      MF_BYPOSITION);
+           DeleteMenu(popup_menus[j].menu,
+                      popup_menus[j].specials_submenu_pos,
+                      MF_BYPOSITION);
+       }
+       if (specials) {
+           InsertMenu(popup_menus[j].menu,
+                      popup_menus[j].specials_submenu_pos,
                       MF_BYPOSITION | MF_SEPARATOR, 0, 0);
-       InsertMenu(m, specials_menu_position,
-                  MF_BYPOSITION | MF_POPUP | MF_ENABLED,
-                  (UINT) p, "Special Command");
+           InsertMenu(popup_menus[j].menu,
+                      popup_menus[j].specials_submenu_pos,
+                      MF_BYPOSITION | MF_POPUP | MF_ENABLED,
+                      (UINT) p, "S&pecial Command");
+       }
     }
 }
 
@@ -942,10 +978,7 @@ void connection_fatal(void *frontend, char *fmt, ...)
     if (cfg.close_on_exit == FORCE_ON)
        PostQuitMessage(1);
     else {
-       session_closed = TRUE;
-       sprintf(morestuff, "%.70s (inactive)", appname);
-       set_icon(NULL, morestuff);
-       set_title(NULL, morestuff);
+       close_session();
     }
 }
 
@@ -990,11 +1023,8 @@ static void enact_pending_netevent(void)
        if (cfg.close_on_exit == FORCE_ON ||
            cfg.close_on_exit == AUTO) PostQuitMessage(0);
        else {
-           char morestuff[100];
+           close_session();
            session_closed = TRUE;
-           sprintf(morestuff, "%.70s (inactive)", appname);
-           set_icon(NULL, morestuff);
-           set_title(NULL, morestuff);
            MessageBox(hwnd, "Connection closed by remote host",
                       appname, MB_OK | MB_ICONINFORMATION);
        }
@@ -1020,6 +1050,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);
+    }
 }
 
 /*
@@ -1067,6 +1129,36 @@ static void init_palette(void)
 }
 
 /*
+ * This is a wrapper to ExtTextOut() to force Windows to display
+ * the precise glyphs we give it. Otherwise it would do its own
+ * bidi and Arabic shaping, and we would end up uncertain which
+ * characters it had put where.
+ */
+static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,
+                         unsigned short *lpString, UINT cbCount,
+                         CONST INT *lpDx)
+{
+
+    GCP_RESULTSW gcpr;
+    char *buffer = snewn(cbCount*2+2, char);
+    char *classbuffer = snewn(cbCount, char);
+    memset(&gcpr, 0, sizeof(gcpr));
+    memset(buffer, 0, cbCount*2+2);
+    memset(classbuffer, GCPCLASS_NEUTRAL, cbCount);
+
+    gcpr.lStructSize = sizeof(gcpr);
+    gcpr.lpGlyphs = (void *)buffer;
+    gcpr.lpClass = classbuffer;
+    gcpr.nGlyphs = cbCount;
+
+    GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr,
+                          FLI_MASK | GCP_CLASSIN);
+
+    ExtTextOut(hdc, x, y, ETO_GLYPH_INDEX | ETO_CLIPPED | ETO_OPAQUE, lprc,
+              buffer, cbCount, lpDx);
+}
+
+/*
  * Initialise all the fonts we will need initially. There may be as many as
  * three or as few as one.  The other (poentially) twentyone fonts are done
  * if/when they are needed.
@@ -1625,9 +1717,9 @@ static Mouse_Button translate_button(Mouse_Button button)
     if (button == MBT_LEFT)
        return MBT_SELECT;
     if (button == MBT_MIDDLE)
-       return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
+       return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND;
     if (button == MBT_RIGHT)
-       return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
+       return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE;
     return 0;                         /* shouldn't happen */
 }
 
@@ -1676,6 +1768,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
     static int ignore_clip = FALSE;
     static int need_backend_resize = FALSE;
     static int fullscr_on_max = FALSE;
+    static UINT last_mousemove = 0;
 
     switch (message) {
       case WM_TIMER:
@@ -1692,7 +1785,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            time_t now;
            time(&now);
            if (now - last_movement > cfg.ping_interval) {
-               back->special(backhandle, TS_PING);
+               if (back)
+                   back->special(backhandle, TS_PING);
                last_movement = now;
            }
        }
@@ -1717,6 +1811,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        show_mouseptr(1);
        PostQuitMessage(0);
        return 0;
+      case WM_COMMAND:
       case WM_SYSCOMMAND:
        switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
          case IDM_SHOWLOG:
@@ -1794,6 +1889,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    sfree(cl);
            }
            break;
+         case IDM_RESTART:
+           if (!back) {
+               logevent(NULL, "----- Session restarted -----");
+               start_backend();
+           }
+
+           break;
          case IDM_RECONF:
            {
                Config prev_cfg;
@@ -1826,7 +1928,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 * Flush the line discipline's edit buffer in the
                 * case where local editing has just been disabled.
                 */
-               ldisc_send(ldisc, NULL, 0, 0);
+               if (ldisc)
+                   ldisc_send(ldisc, NULL, 0, 0);
                if (pal)
                    DeleteObject(pal);
                logpal = NULL;
@@ -1838,7 +1941,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                term_reconfig(term, &cfg);
 
                /* Pass new config data to the back end */
-               back->reconfig(backhandle, &cfg);
+               if (back)
+                   back->reconfig(backhandle, &cfg);
 
                /* Screen size changed ? */
                if (cfg.height != prev_cfg.height ||
@@ -1938,12 +2042,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
          case IDM_COPYALL:
            term_copyall(term);
            break;
+         case IDM_PASTE:
+           term_do_paste(term);
+           break;
          case IDM_CLRSB:
            term_clrsb(term);
            break;
          case IDM_RESET:
            term_pwron(term);
-           ldisc_send(ldisc, NULL, 0, 0);
+           if (ldisc)
+               ldisc_send(ldisc, NULL, 0, 0);
            break;
          case IDM_ABOUT:
            showabout(hwnd);
@@ -1991,7 +2099,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    if (!specials || !specials[j].name)
                        break;
                if (j == i) {
-                   back->special(backhandle, specials[i].code);
+                   if (back)
+                       back->special(backhandle, specials[i].code);
                    net_pending_errors();
                }
            }
@@ -2009,6 +2118,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
       case WM_LBUTTONUP:
       case WM_MBUTTONUP:
       case WM_RBUTTONUP:
+       if (message == WM_RBUTTONDOWN &&
+           ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) {
+           POINT cursorpos;
+
+           show_mouseptr(1);          /* make sure pointer is visible */
+           GetCursorPos(&cursorpos);
+           TrackPopupMenu(popup_menus[CTXMENU].menu,
+                          TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
+                          cursorpos.x, cursorpos.y,
+                          0, hwnd, NULL);
+           break;
+       }
        {
            int button, press;
 
@@ -2047,11 +2168,42 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
             * window, we put up the System menu instead of doing
             * selection.
             */
-           if (is_full_screen() && press && button == MBT_LEFT &&
-               X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
-               SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
-               return 0;
+           {
+               char mouse_on_hotspot = 0;
+               POINT pt;
+
+               GetCursorPos(&pt);
+#ifndef NO_MULTIMON
+               {
+                   HMONITOR mon;
+                   MONITORINFO mi;
+
+                   mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
+
+                   if (mon != NULL) {
+                       mi.cbSize = sizeof(MONITORINFO);
+                       GetMonitorInfo(mon, &mi);
+
+                       if (mi.rcMonitor.left == pt.x &&
+                           mi.rcMonitor.top == pt.y) {
+                           mouse_on_hotspot = 1;
+                       }
+                       CloseHandle(mon);
+                   }
+               }
+#else
+               if (pt.x == 0 && pt.y == 0) {
+                   mouse_on_hotspot = 1;
+               }
+#endif
+               if (is_full_screen() && press &&
+                   button == MBT_LEFT && mouse_on_hotspot) {
+                   SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU,
+                               MAKELPARAM(pt.x, pt.y));
+                   return 0;
+               }
            }
+
            if (press) {
                click(button,
                      TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
@@ -2068,7 +2220,21 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        return 0;
       case WM_MOUSEMOVE:
-       show_mouseptr(1);
+       {
+           /*
+            * Windows seems to like to occasionally send MOUSEMOVE
+            * events even if the mouse hasn't moved. Don't unhide
+            * the mouse pointer in this case.
+            */
+           static WPARAM wp = 0;
+           static LPARAM lp = 0;
+           if (wParam != wp || lParam != lp ||
+               last_mousemove != WM_MOUSEMOVE) {
+               show_mouseptr(1);
+               wp = wParam; lp = lParam;
+               last_mousemove = WM_MOUSEMOVE;
+           }
+       }
        /*
         * Add the mouse position and message time to the random
         * number noise.
@@ -2091,9 +2257,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        return 0;
       case WM_NCMOUSEMOVE:
-       show_mouseptr(1);
+       {
+           static WPARAM wp = 0;
+           static LPARAM lp = 0;
+           if (wParam != wp || lParam != lp ||
+               last_mousemove != WM_NCMOUSEMOVE) {
+               show_mouseptr(1);
+               wp = wParam; lp = lParam;
+               last_mousemove = WM_NCMOUSEMOVE;
+           }
+       }
        noise_ultralight(lParam);
-       return 0;
+       break;
       case WM_IGNORE_CLIP:
        ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
        break;
@@ -2497,7 +2672,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                     * we're sent.
                     */
                    term_seen_key_event(term);
-                   ldisc_send(ldisc, buf, len, 1);
+                   if (ldisc)
+                       ldisc_send(ldisc, buf, len, 1);
                    show_mouseptr(0);
                }
            }
@@ -2545,7 +2721,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 */
                term_seen_key_event(term);
                for (i = 0; i < n; i += 2) {
-                   luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
+                   if (ldisc)
+                       luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
                }
                free(buff);
            }
@@ -2560,11 +2737,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            buf[1] = wParam;
            buf[0] = wParam >> 8;
            term_seen_key_event(term);
-           lpage_send(ldisc, kbd_codepage, buf, 2, 1);
+           if (ldisc)
+               lpage_send(ldisc, kbd_codepage, buf, 2, 1);
        } else {
            char c = (unsigned char) wParam;
            term_seen_key_event(term);
-           lpage_send(ldisc, kbd_codepage, &c, 1, 1);
+           if (ldisc)
+               lpage_send(ldisc, kbd_codepage, &c, 1, 1);
        }
        return (0);
       case WM_CHAR:
@@ -2578,7 +2757,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        {
            char c = (unsigned char)wParam;
            term_seen_key_event(term);
-           lpage_send(ldisc, CP_ACP, &c, 1, 1);
+           if (ldisc)
+               lpage_send(ldisc, CP_ACP, &c, 1, 1);
        }
        return 0;
       case WM_SETCURSOR:
@@ -2587,6 +2767,16 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            return TRUE;
        }
        break;
+      case WM_SYSCOLORCHANGE:
+       if (cfg.system_colour) {
+           /* Refresh palette from system colours. */
+           /* XXX actually this zaps the entire palette. */
+           systopalette();
+           init_palette();
+           /* Force a repaint of the terminal window. */
+           term_invalidate(term);
+       }
+       break;
       case WM_AGENT_CALLBACK:
        {
            struct agent_callback *c = (struct agent_callback *)lParam;
@@ -2711,7 +2901,7 @@ static void sys_cursor_update(void)
  *
  * We are allowed to fiddle with the contents of `text'.
  */
-void do_text(Context ctx, int x, int y, char *text, int len,
+void do_text(Context ctx, int x, int y, wchar_t *text, int len,
             unsigned long attr, int lattr)
 {
     COLORREF fg, bg, t;
@@ -2719,11 +2909,14 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     HDC hdc = ctx;
     RECT line_box;
     int force_manual_underline = 0;
-    int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
-    int char_width = fnt_width;
+    int fnt_width, char_width;
     int text_adjust = 0;
     static int *IpDx = 0, IpDxLEN = 0;
 
+    lattr &= LATTR_MODE;
+
+    char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM));
+
     if (attr & ATTR_WIDE)
        char_width *= 2;
 
@@ -2771,43 +2964,39 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        nfont |= FONT_NARROW;
 
     /* Special hack for the VT100 linedraw glyphs. */
-    if ((attr & CSET_MASK) == 0x2300) {
-       if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
-           switch ((unsigned char) (text[0])) {
-             case 0xBA:
-               text_adjust = -2 * font_height / 5;
-               break;
-             case 0xBB:
-               text_adjust = -1 * font_height / 5;
-               break;
-             case 0xBC:
-               text_adjust = font_height / 5;
-               break;
-             case 0xBD:
-               text_adjust = 2 * font_height / 5;
-               break;
-           }
-           if (lattr == LATTR_TOP || lattr == LATTR_BOT)
-               text_adjust *= 2;
-           attr &= ~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;
-           }
+    if (text[0] >= 0x23BA && text[0] <= 0x23BD) {
+       switch ((unsigned char) (text[0])) {
+         case 0xBA:
+           text_adjust = -2 * font_height / 5;
+           break;
+         case 0xBB:
+           text_adjust = -1 * font_height / 5;
+           break;
+         case 0xBC:
+           text_adjust = font_height / 5;
+           break;
+         case 0xBD:
+           text_adjust = 2 * font_height / 5;
+           break;
+       }
+       if (lattr == LATTR_TOP || lattr == LATTR_BOT)
+           text_adjust *= 2;
+       text[0] = ucsdata.unitab_xterm['q'];
+       if (attr & ATTR_UNDER) {
+           attr &= ~ATTR_UNDER;
+           force_manual_underline = 1;
        }
     }
 
     /* Anything left as an original character set is unprintable. */
-    if (DIRECT_CHAR(attr)) {
-       attr &= ~CSET_MASK;
-       attr |= 0xFF00;
-       memset(text, 0xFD, len);
+    if (DIRECT_CHAR(text[0])) {
+       int i;
+       for (i = 0; i < len; i++)
+           text[i] = 0xFFFD;
     }
 
     /* OEM CP */
-    if ((attr & CSET_MASK) == ATTR_OEMCP)
+    if ((text[0] & CSET_MASK) == CSET_OEMCP)
        nfont |= FONT_OEM;
 
     nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
@@ -2854,7 +3043,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 (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
+    if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) {
        /* Ho Hum, dbcs fonts are a PITA! */
        /* To display on W9x I have to convert to UCS */
        static wchar_t *uni_buf = 0;
@@ -2869,15 +3058,20 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        for(nlen = mptr = 0; mptr<len; mptr++) {
            uni_buf[nlen] = 0xFFFD;
            if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
+               char dbcstext[2];
+               dbcstext[0] = text[mptr] & 0xFF;
+               dbcstext[1] = text[mptr+1] & 0xFF;
                IpDx[nlen] += char_width;
                MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
-                                  text+mptr, 2, uni_buf+nlen, 1);
+                                   dbcstext, 2, uni_buf+nlen, 1);
                mptr++;
            }
            else
            {
+               char dbcstext[1];
+               dbcstext[0] = text[mptr] & 0xFF;
                MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
-                                  text+mptr, 1, uni_buf+nlen, 1);
+                                   dbcstext, 1, uni_buf+nlen, 1);
            }
            nlen++;
        }
@@ -2896,10 +3090,21 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        }
 
        IpDx[0] = -1;
-    } else if (DIRECT_FONT(attr)) {
+    } else if (DIRECT_FONT(text[0])) {
+       static char *directbuf = NULL;
+       static int directlen = 0;
+       int i;
+       if (len > directlen) {
+           directlen = len;
+           directbuf = sresize(directbuf, directlen, char);
+       }
+
+       for (i = 0; i < len; i++)
+           directbuf[i] = text[i] & 0xFF;
+
        ExtTextOut(hdc, x,
                   y - font_height * (lattr == LATTR_BOT) + text_adjust,
-                  ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
+                  ETO_CLIPPED | ETO_OPAQUE, &line_box, directbuf, len, IpDx);
        if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
            SetBkMode(hdc, TRANSPARENT);
 
@@ -2913,7 +3118,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
            ExtTextOut(hdc, x - 1,
                       y - font_height * (lattr ==
                                          LATTR_BOT) + text_adjust,
-                      ETO_CLIPPED, &line_box, text, len, IpDx);
+                      ETO_CLIPPED, &line_box, directbuf, len, IpDx);
        }
     } else {
        /* And 'normal' unicode characters */
@@ -2926,11 +3131,15 @@ void do_text(Context ctx, int x, int y, char *text, int len,
            wbuf = snewn(wlen, WCHAR);
        }
        for (i = 0; i < len; i++)
-           wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
+           wbuf[i] = text[i];
 
-       ExtTextOutW(hdc, x,
+       /* print Glyphs as they are, without Windows' Shaping*/
+       exact_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust,
+                     &line_box, wbuf, len, IpDx);
+/*     ExtTextOutW(hdc, x,
                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
                    ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
+ */
 
        /* And the shadow bold hack. */
        if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
@@ -2957,7 +3166,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     }
 }
 
-void do_cursor(Context ctx, int x, int y, char *text, int len,
+void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
               unsigned long attr, int lattr)
 {
 
@@ -2967,7 +3176,7 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
     int ctype = cfg.cursor_type;
 
     if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
-       if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
+       if (*text != UCSWIDE) {
            do_text(ctx, x, y, text, len, attr, lattr);
            return;
        }
@@ -3044,13 +3253,13 @@ int char_width(Context ctx, int uc) {
     if (!font_dualwidth) return 1;
 
     switch (uc & CSET_MASK) {
-      case ATTR_ASCII:
+      case CSET_ASCII:
        uc = ucsdata.unitab_line[uc & 0xFF];
        break;
-      case ATTR_LINEDRW:
+      case CSET_LINEDRW:
        uc = ucsdata.unitab_xterm[uc & 0xFF];
        break;
-      case ATTR_SCOACS:
+      case CSET_SCOACS:
        uc = ucsdata.unitab_scoacs[uc & 0xFF];
        break;
     }
@@ -3058,12 +3267,12 @@ int char_width(Context ctx, int uc) {
        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)<= '~') 
+       if ((uc&~CSET_MASK) >= ' ' && (uc&~CSET_MASK)<= '~')
            return 1;
 
-       if ( (uc & CSET_MASK) == ATTR_ACP ) {
+       if ( (uc & CSET_MASK) == CSET_ACP ) {
            SelectObject(hdc, fonts[FONT_NORMAL]);
-       } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
+       } else if ( (uc & CSET_MASK) == CSET_OEMCP ) {
            another_font(FONT_OEM);
            if (!fonts[FONT_OEM]) return 0;
 
@@ -3071,8 +3280,8 @@ int char_width(Context ctx, int uc) {
        } else
            return 0;
 
-       if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
-            GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
+       if ( GetCharWidth32(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1 &&
+            GetCharWidth(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1)
            return 0;
     } else {
        /* Speedup, I know of no font where ascii is the wrong width */
@@ -3109,6 +3318,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
     HKL kbd_layout = GetKeyboardLayout(0);
 
+    /* keys is for ToAsciiEx. There's some ick here, see below. */
     static WORD keys[3];
     static int compose_char = 0;
     static WPARAM compose_key = 0;
@@ -3198,8 +3408,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
-       if ((cfg.funky_type == 3 ||
-            (cfg.funky_type <= 1 && term->app_keypad_keys &&
+       if ((cfg.funky_type == FUNKY_VT400 ||
+            (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
              !cfg.no_applic_k))
            && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
 
@@ -3265,8 +3475,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
     /* Sanitize the number pad if not using a PC NumPad */
     if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
-                    && cfg.funky_type != 2)
-       || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
+                    && cfg.funky_type != FUNKY_XTERM)
+       || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) {
        if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
            int nParam = 0;
            switch (wParam) {
@@ -3395,8 +3605,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        if (!left_alt) {
            int xkey = 0;
 
-           if (cfg.funky_type == 3 ||
-               (cfg.funky_type <= 1 &&
+           if (cfg.funky_type == FUNKY_VT400 ||
+               (cfg.funky_type <= FUNKY_LINUX &&
                 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
                  case VK_EXECUTE:
                    xkey = 'P';
@@ -3448,7 +3658,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    xkey = 'n';
                    break;
                  case VK_ADD:
-                   if (cfg.funky_type == 2) {
+                   if (cfg.funky_type == FUNKY_XTERM) {
                        if (shift_state)
                            xkey = 'l';
                        else
@@ -3460,15 +3670,15 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    break;
 
                  case VK_DIVIDE:
-                   if (cfg.funky_type == 2)
+                   if (cfg.funky_type == FUNKY_XTERM)
                        xkey = 'o';
                    break;
                  case VK_MULTIPLY:
-                   if (cfg.funky_type == 2)
+                   if (cfg.funky_type == FUNKY_XTERM)
                        xkey = 'j';
                    break;
                  case VK_SUBTRACT:
-                   if (cfg.funky_type == 2)
+                   if (cfg.funky_type == FUNKY_XTERM)
                        xkey = 'm';
                    break;
 
@@ -3537,6 +3747,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';
@@ -3636,7 +3850,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            break;
        }
        /* Reorder edit keys to physical order */
-       if (cfg.funky_type == 3 && code <= 6)
+       if (cfg.funky_type == FUNKY_VT400 && code <= 6)
            code = "\0\2\1\4\5\3\6"[code];
 
        if (term->vt52_mode && code > 0 && code <= 6) {
@@ -3644,7 +3858,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            return p - output;
        }
 
-       if (cfg.funky_type == 5 &&     /* SCO function keys */
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */
            code >= 11 && code <= 34) {
            char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
            int index = 0;
@@ -3667,7 +3881,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            p += sprintf((char *) p, "\x1B[%c", codes[index]);
            return p - output;
        }
-       if (cfg.funky_type == 5 &&     /* SCO small keypad */
+       if (cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */
            code >= 1 && code <= 6) {
            char codes[] = "HL.FIG";
            if (code == 3) {
@@ -3677,7 +3891,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            }
            return p - output;
        }
-       if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
+       if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
            int offt = 0;
            if (code > 15)
                offt++;
@@ -3690,11 +3904,11 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
            return p - output;
        }
-       if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
+       if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
            p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
            return p - output;
        }
-       if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
+       if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
            if (term->vt52_mode)
                p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
            else
@@ -3796,7 +4010,27 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            keystate[VK_CAPITAL] = 0;
        }
 
-       r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
+       /* XXX how do we know what the max size of the keys array should
+        * be is? There's indication on MS' website of an Inquire/InquireEx
+        * functioning returning a KBINFO structure which tells us. */
+       if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+           /* XXX 'keys' parameter is declared in MSDN documentation as
+            * 'LPWORD lpChar'.
+            * The experience of a French user indicates that on
+            * Win98, WORD[] should be passed in, but on Win2K, it should
+            * be BYTE[]. German WinXP and my Win2K with "US International"
+            * driver corroborate this.
+            * Experimentally I've conditionalised the behaviour on the
+            * Win9x/NT split, but I suspect it's worse than that.
+            * See wishlist item `win-dead-keys' for more horrible detail
+            * and speculations. */
+           BYTE keybs[3];
+           int i;
+           r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout);
+           for (i=0; i<3; i++) keys[i] = keybs[i];
+       } else {
+           r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
+       }
 #ifdef SHOW_TOASCII_RESULT
        if (r == 1 && !key_down) {
            if (alt_sum) {
@@ -3845,7 +4079,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    }
                    keybuf = nc;
                    term_seen_key_event(term);
-                   luni_send(ldisc, &keybuf, 1, 1);
+                   if (ldisc)
+                       luni_send(ldisc, &keybuf, 1, 1);
                    continue;
                }
 
@@ -3856,7 +4091,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        if (in_utf(term) || ucsdata.dbcs_screenfont) {
                            keybuf = alt_sum;
                            term_seen_key_event(term);
-                           luni_send(ldisc, &keybuf, 1, 1);
+                           if (ldisc)
+                               luni_send(ldisc, &keybuf, 1, 1);
                        } else {
                            ch = (char) alt_sum;
                            /*
@@ -3869,12 +4105,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                             * everything we're sent.
                             */
                            term_seen_key_event(term);
-                           ldisc_send(ldisc, &ch, 1, 1);
+                           if (ldisc)
+                               ldisc_send(ldisc, &ch, 1, 1);
                        }
                        alt_sum = 0;
                    } else {
                        term_seen_key_event(term);
-                       lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
+                       if (ldisc)
+                           lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
                    }
                } else {
                    if(capsOn && ch < 0x80) {
@@ -3882,14 +4120,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        cbuf[0] = 27;
                        cbuf[1] = xlat_uskbd2cyrllic(ch);
                        term_seen_key_event(term);
-                       luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
+                       if (ldisc)
+                           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(ldisc, kbd_codepage,
-                                  cbuf+!left_alt, 1+!!left_alt, 1);
+                       if (ldisc)
+                           lpage_send(ldisc, kbd_codepage,
+                                      cbuf+!left_alt, 1+!!left_alt, 1);
                    }
                }
                show_mouseptr(0);
@@ -4398,10 +4638,10 @@ void beep(void *frontend, int mode)
     } else if (mode == BELL_WAVEFILE) {
        if (!PlaySound(cfg.bell_wavefile.path, NULL,
                       SND_ASYNC | SND_FILENAME)) {
-           char buf[sizeof(cfg.bell_wavefile) + 80];
+           char buf[sizeof(cfg.bell_wavefile.path) + 80];
            char otherbuf[100];
            sprintf(buf, "Unable to play sound file\n%s\n"
-                   "Using default sound instead", cfg.bell_wavefile);
+                   "Using default sound instead", cfg.bell_wavefile.path);
            sprintf(otherbuf, "%.70s Sound Error", appname);
            MessageBox(hwnd, buf, otherbuf,
                       MB_OK | MB_ICONEXCLAMATION);
@@ -4535,7 +4775,7 @@ char *get_window_title(void *frontend, int icon)
 /*
  * See if we're in full-screen mode.
  */
-int is_full_screen()
+static int is_full_screen()
 {
     if (!IsZoomed(hwnd))
        return FALSE;
@@ -4549,7 +4789,7 @@ int is_full_screen()
  * one monitor is present. */
 static int get_fullscreen_rect(RECT * ss)
 {
-#ifdef MONITOR_DEFAULTTONEAREST
+#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)
        HMONITOR mon;
        MONITORINFO mi;
        mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
@@ -4574,7 +4814,7 @@ static int get_fullscreen_rect(RECT * ss)
  * Go full-screen. This should only be called when we are already
  * maximised.
  */
-void make_full_screen()
+static void make_full_screen()
 {
     DWORD style;
        RECT ss;
@@ -4608,7 +4848,7 @@ void make_full_screen()
 /*
  * Clear the full-screen attributes.
  */
-void clear_full_screen()
+static void clear_full_screen()
 {
     DWORD oldstyle, style;
 
@@ -4638,7 +4878,7 @@ void clear_full_screen()
 /*
  * Toggle full-screen mode.
  */
-void flip_full_screen()
+static void flip_full_screen()
 {
     if (is_full_screen()) {
        ShowWindow(hwnd, SW_RESTORE);