]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - windows/window.c
Turn off Windows process ACL restriction by default.
[PuTTY.git] / windows / window.c
index 908c1f68bb5fd18a8839b243a0f16f1227bbef67..5ef3a46077157fd33d603f0e9466c2e0df48d041 100644 (file)
@@ -19,6 +19,7 @@
 #include "terminal.h"
 #include "storage.h"
 #include "win_res.h"
+#include "winsecur.h"
 
 #ifndef NO_MULTIMON
 #include <multimon.h>
 #define WHEEL_DELTA 120
 #endif
 
+/* VK_PACKET, used to send Unicode characters in WM_KEYDOWNs */
+#ifndef VK_PACKET
+#define VK_PACKET 0xE7
+#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,
@@ -88,7 +94,7 @@ static void another_font(int);
 static void deinit_fonts(void);
 static void set_input_locale(HKL);
 static void update_savedsess_menu(void);
-static void init_flashwindow(void);
+static void init_winfuncs(void);
 
 static int is_full_screen(void);
 static void make_full_screen(void);
@@ -104,10 +110,6 @@ static int offset_width, offset_height;
 static int was_zoomed = 0;
 static int prev_rows, prev_cols;
   
-static int pending_netevent = 0;
-static WPARAM pend_netevent_wParam = 0;
-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 get_fullscreen_rect(RECT * ss);
@@ -121,7 +123,7 @@ static Backend *back;
 static void *backhandle;
 
 static struct unicode_data ucsdata;
-static int must_close_session, session_closed;
+static int session_closed;
 static int reconfiguring = FALSE;
 
 static const struct telnet_special *specials = NULL;
@@ -140,6 +142,12 @@ static struct {
 enum { SYSMENU, CTXMENU };
 static HMENU savedsess_menu;
 
+struct wm_netevent_params {
+    /* Used to pass data to wm_netevent_callback */
+    WPARAM wParam;
+    LPARAM lParam;
+};
+
 Conf *conf;                           /* exported to windlg.c */
 
 static void conf_cache_data(void);
@@ -168,14 +176,15 @@ struct agent_callback {
 #define FONT_OEMUND    0x22
 #define FONT_OEMBOLDUND 0x23
 
-#define FONT_MAXNO     0x2F
+#define FONT_MAXNO     0x40
 #define FONT_SHIFT     5
 static HFONT fonts[FONT_MAXNO];
 static LOGFONT lfont;
 static int fontflag[FONT_MAXNO];
 static enum {
-    BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
-} bold_mode;
+    BOLD_NONE, BOLD_SHADOW, BOLD_FONT
+} bold_font_mode;
+static int bold_colours;
 static enum {
     UND_LINE, UND_FONT
 } und_mode;
@@ -206,9 +215,23 @@ static int compose_state = 0;
 
 static UINT wm_mousewheel = WM_MOUSEWHEEL;
 
+#define IS_HIGH_VARSEL(wch1, wch2) \
+    ((wch1) == 0xDB40 && ((wch2) >= 0xDD00 && (wch2) <= 0xDDEF))
+#define IS_LOW_VARSEL(wch) \
+    (((wch) >= 0x180B && (wch) <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR */ \
+     ((wch) >= 0xFE00 && (wch) <= 0xFE0F)) /* VARIATION SELECTOR 1-16 */
+
+const int share_can_be_downstream = TRUE;
+const int share_can_be_upstream = TRUE;
+
 /* Dummy routine, only required in plink. */
-void ldisc_update(void *frontend, int echo, int edit)
+void frontend_echoedit_update(void *frontend, int echo, int edit)
+{
+}
+
+int frontend_is_utf8(void *frontend)
 {
+    return ucsdata.line_codepage == CP_UTF8;
 }
 
 char *get_ttymode(void *frontend, const char *mode)
@@ -282,11 +305,10 @@ static void start_backend(void)
        DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
     }
 
-    must_close_session = FALSE;
     session_closed = FALSE;
 }
 
-static void close_session(void)
+static void close_session(void *ignored_context)
 {
     char morestuff[100];
     int i;
@@ -317,24 +339,16 @@ static void close_session(void)
        InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED,
                   IDM_RESTART, "&Restart Session");
     }
-
-    /*
-     * Unset the 'must_close_session' flag, or else we'll come
-     * straight back here the next time we go round the main message
-     * loop - which, worse still, will be immediately (without
-     * blocking) because we've just triggered a WM_SETTEXT by the
-     * window title change above.
-     */
-    must_close_session = FALSE;
 }
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
-    WNDCLASS wndclass;
     MSG msg;
     HRESULT hr;
     int guess_width, guess_height;
 
+    dll_hijacking_protection();
+
     hinst = inst;
     hwnd = NULL;
     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
@@ -343,6 +357,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     InitCommonControls();
 
+    /* Set Explicit App User Model Id so that jump lists don't cause
+       PuTTY to hang on to removable media. */
+
+    set_explicit_app_user_model_id();
+
     /* Ensure a Maximize setting in Explorer doesn't maximise the
      * config box. */
     defuse_showwindow();
@@ -368,7 +387,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     init_help();
 
-    init_flashwindow();
+    init_winfuncs();
 
     conf = conf_new();
 
@@ -445,7 +464,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            HANDLE filemap;
            void *cp;
            unsigned cpsize;
-           if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 1 &&
+           if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 2 &&
                (cp = MapViewOfFile(filemap, FILE_MAP_READ,
                                    0, 0, cpsize)) != NULL) {
                conf_deserialise(conf, cp, cpsize);
@@ -546,8 +565,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                            q += 2;
                        conf_set_int(conf, CONF_protocol, PROT_TELNET);
                        p = q;
-                       while (*p && *p != ':' && *p != '/')
-                           p++;
+                        p += host_strcspn(p, ":/");
                        c = *p;
                        if (*p)
                            *p++ = '\0';
@@ -610,10 +628,21 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                }
            }
 
-           /*
-            * Trim off a colon suffix if it's there.
-            */
-           host[strcspn(host, ":")] = '\0';
+            /*
+             * Trim a colon suffix off the hostname if it's there. In
+             * order to protect unbracketed IPv6 address literals
+             * against this treatment, we do not do this if there's
+             * _more_ than one colon.
+             */
+            {
+                char *c = host_strchr(host, ':');
+                if (c) {
+                    char *d = host_strchr(c+1, ':');
+                    if (!d)
+                        *c = '\0';
+                }
+            }
 
            /*
             * Remove any remaining whitespace.
@@ -633,6 +662,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     }
 
     if (!prev) {
+        WNDCLASSW wndclass;
+
        wndclass.style = 0;
        wndclass.lpfnWndProc = WndProc;
        wndclass.cbClsExtra = 0;
@@ -642,9 +673,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
        wndclass.hbrBackground = NULL;
        wndclass.lpszMenuName = NULL;
-       wndclass.lpszClassName = appname;
+       wndclass.lpszClassName = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
 
-       RegisterClass(&wndclass);
+       RegisterClassW(&wndclass);
     }
 
     memset(&ucsdata, 0, sizeof(ucsdata));
@@ -678,6 +709,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     {
        int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
        int exwinmode = 0;
+        wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
        if (!conf_get_int(conf, CONF_scrollbar))
            winmode &= ~(WS_VSCROLL);
        if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
@@ -686,12 +718,19 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            exwinmode |= WS_EX_TOPMOST;
        if (conf_get_int(conf, CONF_sunken_edge))
            exwinmode |= WS_EX_CLIENTEDGE;
-       hwnd = CreateWindowEx(exwinmode, appname, appname,
-                             winmode, CW_USEDEFAULT, CW_USEDEFAULT,
-                             guess_width, guess_height,
-                             NULL, NULL, inst, NULL);
+       hwnd = CreateWindowExW(exwinmode, uappname, uappname,
+                               winmode, CW_USEDEFAULT, CW_USEDEFAULT,
+                               guess_width, guess_height,
+                               NULL, NULL, inst, NULL);
+        sfree(uappname);
     }
 
+    /*
+     * Initialise the fonts, simultaneously correcting the guesses
+     * for font_{width,height}.
+     */
+    init_fonts(0,0);
+
     /*
      * Initialise the terminal. (We have to do this _after_
      * creating the window, since the terminal is the first thing
@@ -705,12 +744,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
              conf_get_int(conf, CONF_width),
              conf_get_int(conf, CONF_savelines));
 
-    /*
-     * Initialise the fonts, simultaneously correcting the guesses
-     * for font_{width,height}.
-     */
-    init_fonts(0,0);
-
     /*
      * Correct the guesses for extra_{width,height}.
      */
@@ -791,7 +824,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            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) savedsess_menu,
+           AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT_PTR) savedsess_menu,
                       "Sa&ved Sessions");
            AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
            AppendMenu(m, MF_SEPARATOR, 0, 0);
@@ -837,43 +870,76 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     while (1) {
        HANDLE *handles;
        int nhandles, n;
+        DWORD timeout;
+
+        if (toplevel_callback_pending() ||
+            PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+            /*
+             * If we have anything we'd like to do immediately, set
+             * the timeout for MsgWaitForMultipleObjects to zero so
+             * that we'll only do a quick check of our handles and
+             * then get on with whatever that was.
+             *
+             * One such option is a pending toplevel callback. The
+             * other is a non-empty Windows message queue, which you'd
+             * think we could leave to MsgWaitForMultipleObjects to
+             * check for us along with all the handles, but in fact we
+             * can't because once PeekMessage in one iteration of this
+             * loop has removed a message from the queue, the whole
+             * queue is considered uninteresting by the next
+             * invocation of MWFMO. So we check ourselves whether the
+             * message queue is non-empty, and if so, set this timeout
+             * to zero to ensure MWFMO doesn't block.
+             */
+            timeout = 0;
+        } else {
+            timeout = INFINITE;
+            /* The messages seem unreliable; especially if we're being tricky */
+            term_set_focus(term, GetForegroundWindow() == hwnd);
+        }
 
        handles = handle_get_events(&nhandles);
 
-       n = MsgWaitForMultipleObjects(nhandles, handles, FALSE, INFINITE,
-                                     QS_ALLINPUT);
+       n = MsgWaitForMultipleObjects(nhandles, handles, FALSE,
+                                      timeout, QS_ALLINPUT);
 
        if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
            handle_got_event(handles[n - WAIT_OBJECT_0]);
            sfree(handles);
-           if (must_close_session)
-               close_session();
        } else
            sfree(handles);
 
-       while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+       while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT)
                goto finished;         /* two-level break */
 
            if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
-               DispatchMessage(&msg);
-           /* Send the paste buffer if there's anything to send */
-           term_paste(term);
-           /* If there's nothing new in the queue then we can do everything
-            * we've delayed, reading the socket, writing, and repainting
-            * the window.
-            */
-           if (must_close_session)
-               close_session();
-       }
-
-       /* The messages seem unreliable; especially if we're being tricky */
-       term_set_focus(term, GetForegroundWindow() == hwnd);
+               DispatchMessageW(&msg);
 
-       if (pending_netevent)
-           enact_pending_netevent();
+            /*
+             * WM_NETEVENT messages seem to jump ahead of others in
+             * the message queue. I'm not sure why; the docs for
+             * PeekMessage mention that messages are prioritised in
+             * some way, but I'm unclear on which priorities go where.
+             *
+             * Anyway, in practice I observe that WM_NETEVENT seems to
+             * jump to the head of the queue, which means that if we
+             * were to only process one message every time round this
+             * loop, we'd get nothing but NETEVENTs if the server
+             * flooded us with data, and stop responding to any other
+             * kind of window message. So instead, we keep on round
+             * this loop until we've consumed at least one message
+             * that _isn't_ a NETEVENT, or run out of messages
+             * completely (whichever comes first). And we don't go to
+             * run_toplevel_callbacks (which is where the netevents
+             * are actually processed, causing fresh NETEVENT messages
+             * to appear) until we've done this.
+             */
+            if (msg.message != WM_NETEVENT)
+                break;
+       }
 
-       net_pending_errors();
+        run_toplevel_callbacks();
     }
 
     finished:
@@ -985,7 +1051,7 @@ void update_specials_menu(void *frontend)
                saved_menu = new_menu; /* XXX lame stacking */
                new_menu = CreatePopupMenu();
                AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,
-                          (UINT) new_menu, specials[i].name);
+                          (UINT_PTR) new_menu, specials[i].name);
                break;
              case TS_EXITMENU:
                nesting--;
@@ -1010,13 +1076,14 @@ void update_specials_menu(void *frontend)
     for (j = 0; j < lenof(popup_menus); j++) {
        if (specials_menu) {
            /* XXX does this free up all submenus? */
-           DeleteMenu(popup_menus[j].menu, (UINT)specials_menu, MF_BYCOMMAND);
+           DeleteMenu(popup_menus[j].menu, (UINT_PTR)specials_menu,
+                       MF_BYCOMMAND);
            DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND);
        }
        if (new_menu) {
            InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
                       MF_BYCOMMAND | MF_POPUP | MF_ENABLED,
-                      (UINT) new_menu, "S&pecial Command");
+                      (UINT_PTR) new_menu, "S&pecial Command");
            InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
                       MF_BYCOMMAND | MF_SEPARATOR, IDM_SPECIALSEP, 0);
        }
@@ -1081,7 +1148,7 @@ void set_raw_mouse_mode(void *frontend, int activate)
 /*
  * Print a message box and close the connection.
  */
-void connection_fatal(void *frontend, char *fmt, ...)
+void connection_fatal(void *frontend, const char *fmt, ...)
 {
     va_list ap;
     char *stuff, morestuff[100];
@@ -1096,14 +1163,14 @@ void connection_fatal(void *frontend, char *fmt, ...)
     if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON)
        PostQuitMessage(1);
     else {
-       must_close_session = TRUE;
+       queue_toplevel_callback(close_session, NULL);
     }
 }
 
 /*
  * Report an error at the command-line parsing stage.
  */
-void cmdline_error(char *fmt, ...)
+void cmdline_error(const char *fmt, ...)
 {
     va_list ap;
     char *stuff, morestuff[100];
@@ -1120,19 +1187,11 @@ void cmdline_error(char *fmt, ...)
 /*
  * Actually do the job requested by a WM_NETEVENT
  */
-static void enact_pending_netevent(void)
+static void wm_netevent_callback(void *vctx)
 {
-    static int reentering = 0;
-    extern int select_result(WPARAM, LPARAM);
-
-    if (reentering)
-       return;                        /* don't unpend the pending */
-
-    pending_netevent = FALSE;
-
-    reentering = 1;
-    select_result(pend_netevent_wParam, pend_netevent_lParam);
-    reentering = 0;
+    struct wm_netevent_params *params = (struct wm_netevent_params *)vctx;
+    select_result(params->wParam, params->lParam);
+    sfree(vctx);
 }
 
 /*
@@ -1403,8 +1462,9 @@ static void init_fonts(int pick_width, int pick_height)
     for (i = 0; i < FONT_MAXNO; i++)
        fonts[i] = NULL;
 
-    bold_mode = conf_get_int(conf, CONF_bold_colour) ?
-       BOLD_COLOURS : BOLD_FONT;
+    bold_font_mode = conf_get_int(conf, CONF_bold_style) & 1 ?
+       BOLD_FONT : BOLD_NONE;
+    bold_colours = conf_get_int(conf, CONF_bold_style) & 2 ? TRUE : FALSE;
     und_mode = UND_FONT;
 
     font = conf_get_fontspec(conf, CONF_font);
@@ -1433,7 +1493,7 @@ static void init_fonts(int pick_width, int pick_height)
 #define f(i,c,w,u) \
     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
                           c, OUT_DEFAULT_PRECIS, \
-                          CLIP_DEFAULT_PRECIS, quality, \
+                          CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), \
                           FIXED_PITCH | FF_DONTCARE, font->name)
 
     f(FONT_NORMAL, font->charset, fw_dontcare, FALSE);
@@ -1529,7 +1589,7 @@ static void init_fonts(int pick_width, int pick_height)
        }
     }
 
-    if (bold_mode == BOLD_FONT) {
+    if (bold_font_mode == BOLD_FONT) {
        f(FONT_BOLD, font->charset, fw_bold, FALSE);
     }
 #undef f
@@ -1556,9 +1616,9 @@ static void init_fonts(int pick_width, int pick_height)
        fonts[FONT_UNDERLINE] = 0;
     }
 
-    if (bold_mode == BOLD_FONT &&
+    if (bold_font_mode == BOLD_FONT &&
        fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
-       bold_mode = BOLD_SHADOW;
+       bold_font_mode = BOLD_SHADOW;
        DeleteObject(fonts[FONT_BOLD]);
        fonts[FONT_BOLD] = 0;
     }
@@ -1992,7 +2052,7 @@ void notify_remote_exit(void *fe)
            (close_on_exit == AUTO && exitcode != INT_MAX)) {
            PostQuitMessage(0);
        } else {
-           must_close_session = TRUE;
+            queue_toplevel_callback(close_session, NULL);
            session_closed = TRUE;
            /* exitcode == INT_MAX indicates that the connection was closed
             * by a fatal error, so an error box will be coming our way and
@@ -2004,10 +2064,14 @@ void notify_remote_exit(void *fe)
     }
 }
 
-void timer_change_notify(long next)
+void timer_change_notify(unsigned long next)
 {
-    long ticks = next - GETTICKCOUNT();
-    if (ticks <= 0) ticks = 1;        /* just in case */
+    unsigned long now = GETTICKCOUNT();
+    long ticks;
+    if (now - next < INT_MAX)
+       ticks = 0;
+    else
+       ticks = next - now;
     KillTimer(hwnd, TIMING_TIMER_ID);
     SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL);
     timing_next_time = next;
@@ -2034,7 +2098,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
     switch (message) {
       case WM_TIMER:
        if ((UINT_PTR)wParam == TIMING_TIMER_ID) {
-           long next;
+           unsigned long next;
 
            KillTimer(hwnd, TIMING_TIMER_ID);
            if (run_timers(timing_next_time, &next)) {
@@ -2123,7 +2187,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    unsigned int sessno = ((lParam - IDM_SAVED_MIN)
                                           / MENU_SAVED_STEP) + 1;
                    if (sessno < (unsigned)sesslist.nsessions) {
-                       char *session = sesslist.sessions[sessno];
+                       const char *session = sesslist.sessions[sessno];
                        cl = dupprintf("putty @%s", session);
                        inherit_handles = FALSE;
                        freecl = TRUE;
@@ -2144,6 +2208,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                si.lpReserved2 = NULL;
                CreateProcess(b, cl, NULL, NULL, inherit_handles,
                              NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
+                CloseHandle(pi.hProcess);
+                CloseHandle(pi.hThread);
 
                if (filemap)
                    CloseHandle(filemap);
@@ -2183,8 +2249,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                reconfig_result =
                    do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0);
                reconfiguring = FALSE;
-               if (!reconfig_result)
+               if (!reconfig_result) {
+                    conf_free(prev_conf);
                    break;
+                }
 
                conf_cache_data();
 
@@ -2210,9 +2278,10 @@ 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_configure(ldisc, conf);
-               if (ldisc)
-                   ldisc_send(ldisc, NULL, 0, 0);
+               if (ldisc) {
+                    ldisc_configure(ldisc, conf);
+                   ldisc_echoedit_update(ldisc);
+                }
                if (pal)
                    DeleteObject(pal);
                logpal = NULL;
@@ -2315,7 +2384,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                {
                    FontSpec *font = conf_get_fontspec(conf, CONF_font);
                    FontSpec *prev_font = conf_get_fontspec(prev_conf,
-                                                           CONF_font);
+                                                             CONF_font);
 
                    if (!strcmp(font->name, prev_font->name) ||
                        !strcmp(conf_get_str(conf, CONF_line_codepage),
@@ -2327,8 +2396,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                        conf_get_int(prev_conf, CONF_font_quality) ||
                        conf_get_int(conf, CONF_vtmode) !=
                        conf_get_int(prev_conf, CONF_vtmode) ||
-                       conf_get_int(conf, CONF_bold_colour) !=
-                       conf_get_int(prev_conf, CONF_bold_colour) ||
+                       conf_get_int(conf, CONF_bold_style) !=
+                       conf_get_int(prev_conf, CONF_bold_style) ||
                        resize_action == RESIZE_DISABLED ||
                        resize_action == RESIZE_EITHER ||
                        resize_action != conf_get_int(prev_conf,
@@ -2338,7 +2407,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
 
                InvalidateRect(hwnd, NULL, TRUE);
                reset_window(init_lvl);
-               net_pending_errors();
 
                conf_free(prev_conf);
            }
@@ -2355,7 +2423,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
          case IDM_RESET:
            term_pwron(term, TRUE);
            if (ldisc)
-               ldisc_send(ldisc, NULL, 0, 0);
+               ldisc_echoedit_update(ldisc);
            break;
          case IDM_ABOUT:
            showabout(hwnd);
@@ -2401,7 +2469,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    break;
                if (back)
                    back->special(backhandle, specials[i].code);
-               net_pending_errors();
            }
        }
        break;
@@ -2681,20 +2748,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
        }
        return 0;
       case WM_NETEVENT:
-       /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
-        * but the only one that's likely to try to overload us is FD_READ.
-        * This means buffering just one is fine.
-        */
-       if (pending_netevent)
-           enact_pending_netevent();
-
-       pending_netevent = TRUE;
-       pend_netevent_wParam = wParam;
-       pend_netevent_lParam = lParam;
-       if (WSAGETSELECTEVENT(lParam) != FD_READ)
-           enact_pending_netevent();
-
-       net_pending_errors();
+        {
+            /*
+             * To protect against re-entrancy when Windows's recv()
+             * immediately triggers a new WSAAsyncSelect window
+             * message, we don't call select_result directly from this
+             * handler but instead wait until we're back out at the
+             * top level of the message loop.
+             */
+            struct wm_netevent_params *params =
+                snew(struct wm_netevent_params);
+            params->wParam = wParam;
+            params->lParam = lParam;
+            queue_toplevel_callback(wm_netevent_callback, params);
+        }
        return 0;
       case WM_SETFOCUS:
        term_set_focus(term, TRUE);
@@ -2899,7 +2966,21 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                     h = height / font_height;
                     if (h < 1) h = 1;
 
-                    term_size(term, h, w, conf_get_int(conf, CONF_savelines));
+                    if (resizing) {
+                        /*
+                         * As below, if we're in the middle of an
+                         * interactive resize we don't call
+                         * back->size. In Windows 7, this case can
+                         * arise in maximisation as well via the Aero
+                         * snap UI.
+                         */
+                        need_backend_resize = TRUE;
+                        conf_set_int(conf, CONF_height, h);
+                        conf_set_int(conf, CONF_width, w);
+                    } else {
+                        term_size(term, h, w,
+                                  conf_get_int(conf, CONF_savelines));
+                    }
                 }
                 reset_window(0);
             } else if (wParam == SIZE_RESTORED && was_zoomed) {
@@ -2934,7 +3015,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                      */
                    need_backend_resize = TRUE;
                    conf_set_int(conf, CONF_height, h);
-                   conf_set_int(conf, CONF_height, w);
+                   conf_set_int(conf, CONF_width, w);
                 } else {
                     term_size(term, h, w, conf_get_int(conf, CONF_savelines));
                 }
@@ -2966,7 +3047,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            break;
          case SB_THUMBPOSITION:
          case SB_THUMBTRACK:
-           term_scroll(term, 1, HIWORD(wParam));
+           /*
+            * Use GetScrollInfo instead of HIWORD(wParam) to get
+            * 32-bit scroll position.
+            */
+           {
+               SCROLLINFO si;
+
+               si.cbSize = sizeof(si);
+               si.fMask = SIF_TRACKPOS;
+               if (GetScrollInfo(hwnd, SB_VERT, &si) == 0)
+                   si.nTrackPos = HIWORD(wParam);
+               term_scroll(term, 1, si.nTrackPos);
+           }
            break;
        }
        break;
@@ -3012,7 +3105,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            unsigned char buf[20];
            int len;
 
-           if (wParam == VK_PROCESSKEY) { /* IME PROCESS key */
+           if (wParam == VK_PROCESSKEY || /* IME PROCESS key */
+                wParam == VK_PACKET) {     /* 'this key is a Unicode char' */
                if (message == WM_KEYDOWN) {
                    MSG m;
                    m.hwnd = hwnd;
@@ -3024,17 +3118,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            } else {
                len = TranslateKey(message, wParam, lParam, buf);
                if (len == -1)
-                   return DefWindowProc(hwnd, message, wParam, lParam);
+                   return DefWindowProcW(hwnd, message, wParam, lParam);
 
                if (len != 0) {
-                   /*
-                    * Interrupt an ongoing paste. I'm not sure
-                    * this is sensible, but for the moment it's
-                    * preferable to having to faff about buffering
-                    * things.
-                    */
-                   term_nopaste(term);
-
                    /*
                     * We need not bother about stdin backlogs
                     * here, because in GUI PuTTY we can't do
@@ -3050,7 +3136,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                }
            }
        }
-       net_pending_errors();
        return 0;
       case WM_INPUTLANGCHANGE:
        /* wParam == Font number */
@@ -3091,9 +3176,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                 * instead we luni_send the characters one by one.
                 */
                term_seen_key_event(term);
-               for (i = 0; i < n; i += 2) {
-                   if (ldisc)
+               /* don't divide SURROGATE PAIR */
+               if (ldisc) {
+                    for (i = 0; i < n; i += 2) {
+                       WCHAR hs = *(unsigned short *)(buff+i);
+                       if (IS_HIGH_SURROGATE(hs) && i+2 < n) {
+                           WCHAR ls = *(unsigned short *)(buff+i+2);
+                           if (IS_LOW_SURROGATE(ls)) {
+                               luni_send(ldisc, (unsigned short *)(buff+i), 2, 1);
+                               i += 2;
+                               continue;
+                           }
+                       }
                        luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
+                    }
                }
                free(buff);
            }
@@ -3126,10 +3222,21 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
         * we're ready to cope.
         */
        {
-           char c = (unsigned char)wParam;
-           term_seen_key_event(term);
-           if (ldisc)
-               lpage_send(ldisc, CP_ACP, &c, 1, 1);
+            static wchar_t pending_surrogate = 0;
+           wchar_t c = wParam;
+
+            if (IS_HIGH_SURROGATE(c)) {
+                pending_surrogate = c;
+            } else if (IS_SURROGATE_PAIR(pending_surrogate, c)) {
+                wchar_t pair[2];
+                pair[0] = pending_surrogate;
+                pair[1] = c;
+                term_seen_key_event(term);
+                luni_send(ldisc, pair, 2, 1);
+            } else if (!IS_SURROGATE(c)) {
+                term_seen_key_event(term);
+                luni_send(ldisc, &c, 1, 1);
+            }
        }
        return 0;
       case WM_SYSCOLORCHANGE:
@@ -3184,8 +3291,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                } else
                    break;
 
-               if (send_raw_mouse && shift_pressed &&
-                   !(conf_get_int(conf, CONF_mouse_override))) {
+               if (send_raw_mouse &&
+                   !(conf_get_int(conf, CONF_mouse_override) &&
+                      shift_pressed)) {
                    /* Mouse wheel position is in screen coordinates for
                     * some reason */
                    POINT p;
@@ -3197,10 +3305,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                                   TO_CHR_X(p.x),
                                   TO_CHR_Y(p.y), shift_pressed,
                                   control_pressed, is_alt_pressed());
-                       term_mouse(term, b, translate_button(b),
-                                  MA_RELEASE, TO_CHR_X(p.x),
-                                  TO_CHR_Y(p.y), shift_pressed,
-                                  control_pressed, is_alt_pressed());
                    } /* else: not sure when this can fail */
                } else {
                    /* trigger a scroll */
@@ -3217,7 +3321,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
      * Any messages we don't process completely above are passed through to
      * DefWindowProc() for default processing.
      */
-    return DefWindowProc(hwnd, message, wParam, lParam);
+    return DefWindowProcW(hwnd, message, wParam, lParam);
 }
 
 /*
@@ -3292,9 +3396,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
     int text_adjust = 0;
     int xoffset = 0;
     int maxlen, remaining, opaque;
+    int is_cursor = FALSE;
     static int *lpDx = NULL;
     static int lpDx_len = 0;
     int *lpDx_maybe;
+    int len2; /* for SURROGATE PAIR */
 
     lattr &= LATTR_MODE;
 
@@ -3314,11 +3420,9 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
 
     if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) {
        attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS);
-       if (bold_mode == BOLD_COLOURS)
-           attr &= ~ATTR_BOLD;
-
        /* cursor fg and bg */
        attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT);
+        is_cursor = TRUE;
     }
 
     nfont = 0;
@@ -3339,6 +3443,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
     if (attr & ATTR_NARROW)
        nfont |= FONT_NARROW;
 
+#ifdef USES_VTLINE_HACK
     /* Special hack for the VT100 linedraw glyphs. */
     if (text[0] >= 0x23BA && text[0] <= 0x23BD) {
        switch ((unsigned char) (text[0])) {
@@ -3363,9 +3468,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
            force_manual_underline = 1;
        }
     }
+#endif
 
     /* Anything left as an original character set is unprintable. */
-    if (DIRECT_CHAR(text[0])) {
+    if (DIRECT_CHAR(text[0]) &&
+        (len < 2 || !IS_SURROGATE_PAIR(text[0], text[1]))) {
        int i;
        for (i = 0; i < len; i++)
            text[i] = 0xFFFD;
@@ -3377,7 +3484,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
 
     nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
     nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
-    if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
+    if (bold_font_mode == BOLD_FONT && (attr & ATTR_BOLD))
        nfont |= FONT_BOLD;
     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
        nfont |= FONT_UNDERLINE;
@@ -3397,11 +3504,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
        nfg = nbg;
        nbg = t;
     }
-    if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) {
+    if (bold_colours && (attr & ATTR_BOLD) && !is_cursor) {
        if (nfg < 16) nfg |= 8;
        else if (nfg >= 256) nfg |= 1;
     }
-    if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) {
+    if (bold_colours && (attr & ATTR_BLINK)) {
        if (nbg < 16) nbg |= 8;
        else if (nbg >= 256) nbg |= 1;
     }
@@ -3418,6 +3525,24 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
     line_box.top = y;
     line_box.right = x + char_width * len;
     line_box.bottom = y + font_height;
+    /* adjust line_box.right for SURROGATE PAIR & VARIATION SELECTOR */
+    {
+       int i;
+       int rc_width = 0;
+       for (i = 0; i < len ; i++) {
+           if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) {
+               i++;
+           } else if (i+1 < len && IS_SURROGATE_PAIR(text[i], text[i+1])) {
+               rc_width += char_width;
+               i++;
+           } else if (IS_LOW_VARSEL(text[i])) {
+               /* do nothing */
+            } else {
+               rc_width += char_width;
+            }
+       }
+       line_box.right = line_box.left + rc_width;
+    }
 
     /* Only want the left half of double width lines */
     if (line_box.right > font_width*term->cols+offset_width)
@@ -3448,19 +3573,47 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
 
     opaque = TRUE;                     /* start by erasing the rectangle */
     for (remaining = len; remaining > 0;
-         text += len, remaining -= len, x += char_width * len) {
+         text += len, remaining -= len, x += char_width * len2) {
         len = (maxlen < remaining ? maxlen : remaining);
-
-        if (len > lpDx_len) {
-            if (len > lpDx_len) {
-                lpDx_len = len * 9 / 8 + 16;
-                lpDx = sresize(lpDx, lpDx_len, int);
-            }
+        /* don't divide SURROGATE PAIR and VARIATION SELECTOR */
+        len2 = len;
+        if (maxlen == 1) {
+            if (remaining >= 1 && IS_SURROGATE_PAIR(text[0], text[1]))
+                len++;
+            if (remaining-len >= 1 && IS_LOW_VARSEL(text[len]))
+                len++;
+            else if (remaining-len >= 2 &&
+                     IS_HIGH_VARSEL(text[len], text[len+1]))
+                len += 2;
         }
+
+       if (len > lpDx_len) {
+           lpDx_len = len * 9 / 8 + 16;
+           lpDx = sresize(lpDx, lpDx_len, int);
+
+           if (lpDx_maybe) lpDx_maybe = lpDx;
+       }
+
         {
             int i;
-            for (i = 0; i < len; i++)
+            /* only last char has dx width in SURROGATE PAIR and
+             * VARIATION sequence */
+            for (i = 0; i < len; i++) {
                 lpDx[i] = char_width;
+                if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) {
+                    if (i > 0) lpDx[i-1] = 0;
+                    lpDx[i] = 0;
+                    i++;
+                    lpDx[i] = char_width;
+                } else if (i+1 < len && IS_SURROGATE_PAIR(text[i],text[i+1])) {
+                    lpDx[i] = 0;
+                    i++;
+                    lpDx[i] = char_width;
+                } else if (IS_LOW_VARSEL(text[i])) {
+                    if (i > 0) lpDx[i-1] = 0;
+                    lpDx[i] = char_width;
+                }
+            }
         }
 
         /* We're using a private area for direct to font. (512 chars.) */
@@ -3505,7 +3658,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
                         ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
                         &line_box, uni_buf, nlen,
                         lpDx_maybe);
-            if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+            if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
                 SetBkMode(hdc, TRANSPARENT);
                 ExtTextOutW(hdc, x + xoffset - 1,
                             y - font_height * (lattr ==
@@ -3530,7 +3683,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
                        y - font_height * (lattr == LATTR_BOT) + text_adjust,
                        ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
                        &line_box, directbuf, len, lpDx_maybe);
-            if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+            if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
                 SetBkMode(hdc, TRANSPARENT);
 
                 /* GRR: This draws the character outside its box and
@@ -3569,7 +3722,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
                             opaque && !(attr & TATTR_COMBINING));
 
             /* And the shadow bold hack. */
-            if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+            if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
                 SetBkMode(hdc, TRANSPARENT);
                 ExtTextOutW(hdc, x + xoffset - 1,
                             y - font_height * (lattr ==
@@ -3594,8 +3747,8 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
            dec = dec * 2 - font_height;
 
        oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
-       MoveToEx(hdc, x, y + dec, NULL);
-       LineTo(hdc, x + len * char_width, y + dec);
+       MoveToEx(hdc, line_box.left, line_box.top + dec, NULL);
+       LineTo(hdc, line_box.right, line_box.top + dec);
        oldpen = SelectObject(hdc, oldpen);
        DeleteObject(oldpen);
     }
@@ -3609,9 +3762,35 @@ void do_text(Context ctx, int x, int y, wchar_t *text, int len,
 {
     if (attr & TATTR_COMBINING) {
        unsigned long a = 0;
-       attr &= ~TATTR_COMBINING;
+       int len0 = 1;
+        /* don't divide SURROGATE PAIR and VARIATION SELECTOR */
+       if (len >= 2 && IS_SURROGATE_PAIR(text[0], text[1]))
+           len0 = 2;
+       if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) {
+           attr &= ~TATTR_COMBINING;
+           do_text_internal(ctx, x, y, text, len0+1, attr, lattr);
+           text += len0+1;
+           len -= len0+1;
+           a = TATTR_COMBINING;
+       } else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) {
+           attr &= ~TATTR_COMBINING;
+           do_text_internal(ctx, x, y, text, len0+2, attr, lattr);
+           text += len0+2;
+           len -= len0+2;
+           a = TATTR_COMBINING;
+       } else {
+            attr &= ~TATTR_COMBINING;
+        }
+
        while (len--) {
-           do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
+           if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) {
+               do_text_internal(ctx, x, y, text, 2, attr | a, lattr);
+               len--;
+               text++;
+           } else {
+                do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
+            }
+
            text++;
            a = TATTR_COMBINING;
        }
@@ -3757,6 +3936,17 @@ int char_width(Context ctx, int uc) {
     return ibuf;
 }
 
+DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));
+DECL_WINDOWS_FUNCTION(static, BOOL, ToUnicodeEx,
+                      (UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL));
+
+static void init_winfuncs(void)
+{
+    HMODULE user32_module = load_system32_dll("user32.dll");
+    GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);
+    GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx);
+}
+
 /*
  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
  * codes. Returns number of bytes used, zero to drop the message,
@@ -3778,8 +3968,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 wchar_t keys_unicode[3];
     static int compose_char = 0;
     static WPARAM compose_keycode = 0;
 
@@ -3831,12 +4020,12 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                else if (ch)
                    debug((", $%02x", ch));
 
-               if (keys[0])
-                   debug((", KB0=%02x", keys[0]));
-               if (keys[1])
-                   debug((", KB1=%02x", keys[1]));
-               if (keys[2])
-                   debug((", KB2=%02x", keys[2]));
+               if (keys_unicode[0])
+                   debug((", KB0=%04x", keys_unicode[0]));
+               if (keys_unicode[1])
+                   debug((", KB1=%04x", keys_unicode[1]));
+               if (keys_unicode[2])
+                   debug((", KB2=%04x", keys_unicode[2]));
 
                if ((keystate[VK_SHIFT] & 0x80) != 0)
                    debug((", S"));
@@ -4451,7 +4640,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        /* 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) {
+       if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT && p_ToUnicodeEx) {
+           r = p_ToUnicodeEx(wParam, scan, keystate, keys_unicode,
+                              lenof(keys_unicode), 0, kbd_layout);
+       } else {
            /* XXX 'keys' parameter is declared in MSDN documentation as
             * 'LPWORD lpChar'.
             * The experience of a French user indicates that on
@@ -4462,12 +4654,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
             * 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 {
+           static WORD keys[3];
+           static BYTE keysb[3];
            r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
+           if (r > 0) {
+               for (i = 0; i < r; i++) {
+                   keysb[i] = (BYTE)keys[i];
+               }
+               MultiByteToWideChar(CP_ACP, 0, (LPCSTR)keysb, r,
+                                    keys_unicode, lenof(keys_unicode));
+           }
        }
 #ifdef SHOW_TOASCII_RESULT
        if (r == 1 && !key_down) {
@@ -4477,13 +4674,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                else
                    debug((", LCH(%d)", alt_sum));
            } else {
-               debug((", ACH(%d)", keys[0]));
+               debug((", ACH(%d)", keys_unicode[0]));
            }
        } else if (r > 0) {
            int r1;
            debug((", ASC("));
            for (r1 = 0; r1 < r; r1++) {
-               debug(("%s%d", r1 ? "," : "", keys[r1]));
+               debug(("%s%d", r1 ? "," : "", keys_unicode[r1]));
            }
            debug((")"));
        }
@@ -4491,27 +4688,20 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        if (r > 0) {
            WCHAR keybuf;
 
-           /*
-            * Interrupt an ongoing paste. I'm not sure this is
-            * sensible, but for the moment it's preferable to
-            * having to faff about buffering things.
-            */
-           term_nopaste(term);
-
            p = output;
            for (i = 0; i < r; i++) {
-               unsigned char ch = (unsigned char) keys[i];
+               wchar_t wch = keys_unicode[i];
 
-               if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
-                   compose_char = ch;
+               if (compose_state == 2 && wch >= ' ' && wch < 0x80) {
+                   compose_char = wch;
                    compose_state++;
                    continue;
                }
-               if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
+               if (compose_state == 3 && wch >= ' ' && wch < 0x80) {
                    int nc;
                    compose_state = 0;
 
-                   if ((nc = check_compose(compose_char, ch)) == -1) {
+                   if ((nc = check_compose(compose_char, wch)) == -1) {
                        MessageBeep(MB_ICONHAND);
                        return 0;
                    }
@@ -4532,7 +4722,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                            if (ldisc)
                                luni_send(ldisc, &keybuf, 1, 1);
                        } else {
-                           ch = (char) alt_sum;
+                           char ch = (char) alt_sum;
                            /*
                             * We need not bother about stdin
                             * backlogs here, because in GUI PuTTY
@@ -4550,40 +4740,39 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                    } else {
                        term_seen_key_event(term);
                        if (ldisc)
-                           lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
+                           luni_send(ldisc, &wch, 1, 1);
                    }
                } else {
-                   if(capsOn && ch < 0x80) {
+                   if(capsOn && wch < 0x80) {
                        WCHAR cbuf[2];
                        cbuf[0] = 27;
-                       cbuf[1] = xlat_uskbd2cyrllic(ch);
+                       cbuf[1] = xlat_uskbd2cyrllic(wch);
                        term_seen_key_event(term);
                        if (ldisc)
                            luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
                    } else {
-                       char cbuf[2];
+                       WCHAR cbuf[2];
                        cbuf[0] = '\033';
-                       cbuf[1] = ch;
+                       cbuf[1] = wch;
                        term_seen_key_event(term);
                        if (ldisc)
-                           lpage_send(ldisc, kbd_codepage,
-                                      cbuf+!left_alt, 1+!!left_alt, 1);
+                           luni_send(ldisc, cbuf +!left_alt, 1+!!left_alt, 1);
                    }
                }
                show_mouseptr(0);
            }
 
            /* This is so the ALT-Numpad and dead keys work correctly. */
-           keys[0] = 0;
+           keys_unicode[0] = 0;
 
            return p - output;
        }
        /* If we're definitly not building up an ALT-54321 then clear it */
        if (!left_alt)
-           keys[0] = 0;
+           keys_unicode[0] = 0;
        /* If we will be using alt_sum fix the 256s */
-       else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
-           keys[0] = 10;
+       else if (keys_unicode[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
+           keys_unicode[0] = 10;
     }
 
     /*
@@ -4670,7 +4859,7 @@ void palette_set(void *frontend, int n, int r, int g, int b)
 {
     if (n >= 16)
        n += 256 - 16;
-    if (n > NALLCOLOURS)
+    if (n >= NALLCOLOURS)
        return;
     real_palette_set(n, r, g, b);
     if (pal) {
@@ -4770,10 +4959,17 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
            GlobalFree(clipdata2);
        return;
     }
-    if (!(lock = GlobalLock(clipdata)))
+    if (!(lock = GlobalLock(clipdata))) {
+        GlobalFree(clipdata);
+        GlobalFree(clipdata2);
        return;
-    if (!(lock2 = GlobalLock(clipdata2)))
+    }
+    if (!(lock2 = GlobalLock(clipdata2))) {
+        GlobalUnlock(clipdata);
+        GlobalFree(clipdata);
+        GlobalFree(clipdata2);
        return;
+    }
 
     memcpy(lock, data, len * sizeof(wchar_t));
     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
@@ -4823,7 +5019,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
                    bgcolour = tmpcolour;
                }
 
-               if (bold_mode == BOLD_COLOURS && (attr[i] & ATTR_BOLD)) {
+               if (bold_colours && (attr[i] & ATTR_BOLD)) {
                    if (fgcolour  <   8)        /* ANSI colours */
                        fgcolour +=   8;
                    else if (fgcolour >= 256)   /* Default colours */
@@ -4914,7 +5110,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
                    bgcolour = tmpcolour;
                }
 
-               if (bold_mode == BOLD_COLOURS && (attr[tindex] & ATTR_BOLD)) {
+               if (bold_colours && (attr[tindex] & ATTR_BOLD)) {
                    if (fgcolour  <   8)            /* ANSI colours */
                        fgcolour +=   8;
                    else if (fgcolour >= 256)       /* Default colours */
@@ -4931,7 +5127,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
                 /*
                  * Collect other attributes
                  */
-               if (bold_mode != BOLD_COLOURS)
+               if (bold_font_mode != BOLD_NONE)
                    attrBold  = attr[tindex] & ATTR_BOLD;
                else
                    attrBold  = 0;
@@ -4950,7 +5146,7 @@ void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_des
                        bgcolour  = -1;             /* No coloring */
 
                    if (fgcolour >= 256) {          /* Default colour */
-                       if (bold_mode == BOLD_COLOURS && (fgcolour & 1) && bgcolour == -1)
+                       if (bold_colours && (fgcolour & 1) && bgcolour == -1)
                            attrBold = ATTR_BOLD;   /* Emphasize text with bold attribute */
 
                        fgcolour  = -1;             /* No coloring */
@@ -5183,7 +5379,7 @@ void optimised_move(void *frontend, int to, int from, int lines)
 /*
  * Print a message box and perform a fatal exit.
  */
-void fatalbox(char *fmt, ...)
+void fatalbox(const char *fmt, ...)
 {
     va_list ap;
     char *stuff, morestuff[100];
@@ -5200,7 +5396,7 @@ void fatalbox(char *fmt, ...)
 /*
  * Print a modal (Really Bad) message box and perform a fatal exit.
  */
-void modalfatalbox(char *fmt, ...)
+void modalfatalbox(const char *fmt, ...)
 {
     va_list ap;
     char *stuff, morestuff[100];
@@ -5215,12 +5411,20 @@ void modalfatalbox(char *fmt, ...)
     cleanup_exit(1);
 }
 
-DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));
-
-static void init_flashwindow(void)
+/*
+ * Print a message box and don't close the connection.
+ */
+void nonfatal(const char *fmt, ...)
 {
-    HMODULE user32_module = load_system32_dll("user32.dll");
-    GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);
+    va_list ap;
+    char *stuff, morestuff[100];
+
+    va_start(ap, fmt);
+    stuff = dupvprintf(fmt, ap);
+    va_end(ap);
+    sprintf(morestuff, "%.70s Error", appname);
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
+    sfree(stuff);
 }
 
 static BOOL flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout)
@@ -5246,9 +5450,9 @@ static int flashing = 0;
  * Timer for platforms where we must maintain window flashing manually
  * (e.g., Win95).
  */
-static void flash_window_timer(void *ctx, long now)
+static void flash_window_timer(void *ctx, unsigned long now)
 {
-    if (flashing && now - next_flash >= 0) {
+    if (flashing && now == next_flash) {
        flash_window(1);
     }
 }
@@ -5612,7 +5816,12 @@ int from_backend_untrusted(void *frontend, const char *data, int len)
     return term_data_untrusted(term, data, len);
 }
 
-int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
+int from_backend_eof(void *frontend)
+{
+    return TRUE;   /* do respond to incoming EOF with outgoing */
+}
+
+int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen)
 {
     int ret;
     ret = cmdline_get_passwd_input(p, in, inlen);