Wez Furlong's patch to add xterm mouse reporting and proper mouse
[u/mdw/putty] / window.c
index 43e305f..6f3eb49 100644 (file)
--- a/window.c
+++ b/window.c
@@ -1,5 +1,7 @@
 #include <windows.h>
+#include <imm.h>
 #include <commctrl.h>
+#include <mmsystem.h>
 #ifndef AUTO_WINSOCK
 #ifdef WINSOCK_TWO
 #include <winsock2.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <time.h>
 
 #define PUTTY_DO_GLOBALS                      /* actually _define_ globals */
 #include "putty.h"
+#include "winstuff.h"
 #include "storage.h"
 #include "win_res.h"
 
 #define IDM_TEL_EOF   0x0130
 #define IDM_ABOUT     0x0140
 #define IDM_SAVEDSESS 0x0150
+#define IDM_COPYALL   0x0160
 
+#define IDM_SESSLGP   0x0250  /* log type printable */
+#define IDM_SESSLGA   0x0260  /* log type all chars */
+#define IDM_SESSLGE   0x0270  /* log end */
 #define IDM_SAVED_MIN 0x1000
 #define IDM_SAVED_MAX 0x2000
 
 #define WM_IGNORE_SIZE (WM_XUSER + 1)
 #define WM_IGNORE_CLIP (WM_XUSER + 2)
 
+/* Needed for Chinese support and apparently not always defined. */
+#ifndef VK_PROCESSKEY
+#define VK_PROCESSKEY 0xE5
+#endif
+
 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
 static void cfgtopalette(void);
@@ -57,6 +70,8 @@ static WPARAM pend_netevent_wParam = 0;
 static LPARAM pend_netevent_lParam = 0;
 static void enact_pending_netevent(void);
 
+static time_t last_movement = 0;
+
 #define FONT_NORMAL 0
 #define FONT_BOLD 1
 #define FONT_UNDERLINE 2
@@ -88,13 +103,16 @@ static HBITMAP caretbm;
 static int dbltime, lasttime, lastact;
 static Mouse_Button lastbtn;
 
+/* this allows xterm-style mouse handling. */
+static int send_raw_mouse = 0;
+static int wheel_accumulator = 0;
+
 static char *window_name, *icon_name;
 
-static Ldisc *real_ldisc;
+static int compose_state = 0;
 
-void begin_session(void) {
-    ldisc = real_ldisc;
-}
+/* Dummy routine, only required in plink. */
+void ldisc_update(int echo, int edit) {}
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     static char appname[] = "PuTTY";
@@ -104,7 +122,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     MSG msg;
     int guess_width, guess_height;
 
-    putty_inst = inst;
+    hinst = inst;
     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
 
     winsock_ver = MAKEWORD(1, 1);
@@ -120,9 +138,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        return 1;
     }
     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
+    sk_init();
 
     InitCommonControls();
 
+    /* Ensure a Maximize setting in Explorer doesn't maximise the
+     * config box. */
+    defuse_showwindow();
+
     /*
      * Process the command line.
      */
@@ -131,8 +154,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
 
        default_protocol = DEFAULT_PROTOCOL;
        default_port = DEFAULT_PORT;
+       cfg.logtype = LGTYP_NONE;
 
-       do_defaults(NULL);
+       do_defaults(NULL, &cfg);
 
        p = cmdline;
        while (*p && isspace(*p)) p++;
@@ -151,11 +175,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                tolower(p[2]) == 'h') {
                default_protocol = cfg.protocol = PROT_SSH;
                default_port = cfg.port = 22;
-           } else if (q == p + 3 &&
-               tolower(p[0]) == 'l' &&
-               tolower(p[1]) == 'o' &&
-               tolower(p[2]) == 'g') {
-                logfile = "putty.log";
            } else if (q == p + 7 &&
                tolower(p[0]) == 'c' &&
                tolower(p[1]) == 'l' &&
@@ -190,7 +209,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         * An initial @ means to activate a saved session.
         */
        if (*p == '@') {
-           do_defaults (p+1);
+           int i = strlen(p);
+           while (i > 1 && isspace(p[i-1]))
+               i--;
+           p[i] = '\0';
+           do_defaults (p+1, &cfg);
            if (!*cfg.host && !do_config()) {
                WSACleanup();
                return 0;
@@ -292,10 +315,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         }
     }
 
-    real_ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
-    /* To start with, we use the simple line discipline, so we can
-     * type passwords etc without fear of them being echoed... */
-    ldisc = &ldisc_simple;
+    /* Check for invalid Port number (i.e. zero) */
+    if (cfg.port == 0) {
+        MessageBox(NULL, "Invalid Port Number",
+                  "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
+        WSACleanup();
+        return 1;
+    }
 
     if (!prev) {
        wndclass.style         = 0;
@@ -345,15 +371,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     }
 
     {
-       int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
-       if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
-       if (cfg.locksize)   winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
-       hwnd = CreateWindow (appname, appname,
-                           winmode,
-                           CW_USEDEFAULT, CW_USEDEFAULT,
-                           guess_width, guess_height,
-                           NULL, NULL, inst, NULL);
-    }
+       int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
+        int exwinmode = 0;
+       if (!cfg.scrollbar)  winmode &= ~(WS_VSCROLL);
+       if (cfg.locksize)    winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
+        if (cfg.alwaysontop) exwinmode |= WS_EX_TOPMOST;
+       if (cfg.sunken_edge) exwinmode |= WS_EX_CLIENTEDGE;
+        hwnd = CreateWindowEx (exwinmode, appname, appname,
+                              winmode, CW_USEDEFAULT, CW_USEDEFAULT,
+                              guess_width, guess_height,
+                              NULL, NULL, inst, NULL);
+     }
 
     /*
      * Initialise the fonts, simultaneously correcting the guesses
@@ -390,10 +418,12 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     {
         char *bits;
         int size = (font_width+15)/16 * 2 * font_height; 
-        bits = calloc(size, 1);
+        bits = smalloc(size);
+        memset(bits, 0, size);
         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
-        free(bits);
+        sfree(bits);
     }
+    CreateCaret(hwnd, caretbm, font_width, font_height);
 
     /*
      * Initialise the scroll bar.
@@ -415,19 +445,26 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
      */
     {
        char *error;
-       char msg[1024];
+       char msg[1024], *title;
        char *realhost;
 
-       error = back->init (hwnd, cfg.host, cfg.port, &realhost);
+       error = back->init (cfg.host, cfg.port, &realhost);
        if (error) {
-           sprintf(msg, "Unable to open connection:\n%s", error);
+           sprintf(msg, "Unable to open connection to\n"
+                   "%.800s\n"
+                   "%s", cfg.host, error);
            MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
            return 0;
        }
        window_name = icon_name = NULL;
-       sprintf(msg, "%s - PuTTY", realhost);
-       set_title (msg);
-       set_icon (msg);
+        if (*cfg.wintitle) {
+            title = cfg.wintitle;
+        } else {
+            sprintf(msg, "%s - PuTTY", realhost);
+            title = msg;
+        }
+       set_title (title);
+       set_icon (title);
     }
 
     session_closed = FALSE;
@@ -442,7 +479,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
      * Prepare the mouse handler.
      */
     lastact = MA_NOTHING;
-    lastbtn = MB_NOTHING;
+    lastbtn = MBT_NOTHING;
     dbltime = GetDoubleClickTime();
 
     /*
@@ -477,15 +514,16 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        }
        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_NEWSESS, "Ne&w Session...");
        AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
        s = CreateMenu();
        get_sesslist(TRUE);
        for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
          AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , 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_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);
@@ -498,6 +536,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     ShowWindow (hwnd, show);
 
     /*
+     * Open the initial log file if there is one.
+     */
+    logfopen();
+
+    /*
      * Set the palette up.
      */
     pal = NULL;
@@ -507,10 +550,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     has_focus = (GetForegroundWindow() == hwnd);
     UpdateWindow (hwnd);
 
+    if (GetMessage (&msg, NULL, 0, 0) == 1)
     {
        int timer_id = 0, long_timer = 0;
 
-       while (GetMessage (&msg, NULL, 0, 0) == 1) {
+       while (msg.message != WM_QUIT) {
            /* Sometimes DispatchMessage calls routines that use their own
             * GetMessage loop, setup this timer so we get some control back.
             *
@@ -523,11 +567,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
            }
            if(!timer_id)
                timer_id = SetTimer(hwnd, 1, 20, NULL);
-           DispatchMessage (&msg);
+            if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
+                DispatchMessage (&msg);
 
-           /* This is too fast, but I'll leave it for now 'cause it shows
-            * how often term_update is called (far too often at times!)
-            */
+           /* Make sure we blink everything that needs it. */
            term_blink(0);
 
            /* Send the paste buffer if there's anything to send */
@@ -537,31 +580,47 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
             * we've delayed, reading the socket, writing, and repainting
             * the window.
             */
-           if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
-               if (pending_netevent) {
-                   enact_pending_netevent();
+           if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+               continue;
 
-                   term_blink(1);
-               }
-           } else continue;
-           if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
-               if (timer_id) {
-                   KillTimer(hwnd, timer_id);
-                   timer_id = 0;
-               }
-                HideCaret(hwnd);
-               if (inbuf_head)
-                   term_out();
-               term_update();
-                ShowCaret(hwnd);
-               if (!has_focus)
-                  timer_id = SetTimer(hwnd, 1, 2000, NULL);
-               else if (cfg.blinktext)
-                  timer_id = SetTimer(hwnd, 1, 250, NULL);
-               else
-                  timer_id = SetTimer(hwnd, 1, 500, NULL);
-               long_timer = 1;
+           if (pending_netevent) {
+               enact_pending_netevent();
+
+               /* Force the cursor blink on */
+               term_blink(1);
+
+               if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+                   continue;
+           }
+
+           /* Okay there is now nothing to do so we make sure the screen is
+            * completely up to date then tell windows to call us in a little 
+            * while.
+            */
+           if (timer_id) {
+               KillTimer(hwnd, timer_id);
+               timer_id = 0;
            }
+            HideCaret(hwnd);
+           if (inbuf_head)
+               term_out();
+           term_update();
+            ShowCaret(hwnd);
+           if (in_vbell)
+              /* Hmm, term_update didn't want to do an update too soon ... */
+              timer_id = SetTimer(hwnd, 1, 50, NULL);
+           else if (!has_focus)
+              timer_id = SetTimer(hwnd, 1, 2000, NULL);
+           else
+              timer_id = SetTimer(hwnd, 1, 100, NULL);
+           long_timer = 1;
+       
+           /* There's no point rescanning everything in the message queue
+            * so we do an apparently unnecessary wait here
+            */
+           WaitMessage();
+           if (GetMessage (&msg, NULL, 0, 0) != 1)
+               break;
        }
     }
 
@@ -590,6 +649,37 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
 }
 
 /*
+ * Set up, or shut down, an AsyncSelect. Called from winnet.c.
+ */
+char *do_select(SOCKET skt, int startup) {
+    int msg, events;
+    if (startup) {
+       msg = WM_NETEVENT;
+       events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
+    } else {
+       msg = events = 0;
+    }
+    if (!hwnd)
+       return "do_select(): internal error (hwnd==NULL)";
+    if (WSAAsyncSelect (skt, hwnd, msg, events) == SOCKET_ERROR) {
+        switch (WSAGetLastError()) {
+          case WSAENETDOWN: return "Network is down";
+          default: return "WSAAsyncSelect(): unknown error";
+        }
+    }
+    return NULL;
+}
+
+/*
+ * set or clear the "raw mouse message" mode
+ */
+void set_raw_mouse_mode(int activate)
+{
+    send_raw_mouse = activate;
+    SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
+}
+
+/*
  * Print a message box and close the connection.
  */
 void connection_fatal(char *fmt, ...) {
@@ -600,7 +690,7 @@ void connection_fatal(char *fmt, ...) {
     vsprintf(stuff, fmt, ap);
     va_end(ap);
     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
-    if (cfg.close_on_exit)
+    if (cfg.close_on_exit == COE_ALWAYS)
         PostQuitMessage(1);
     else {
         session_closed = TRUE;
@@ -612,8 +702,9 @@ void connection_fatal(char *fmt, ...) {
  * Actually do the job requested by a WM_NETEVENT
  */
 static void enact_pending_netevent(void) {
-    int i;
     static int reentering = 0;
+    extern int select_result(WPARAM, LPARAM);
+    int ret;
 
     if (reentering)
         return;                        /* don't unpend the pending */
@@ -621,29 +712,20 @@ static void enact_pending_netevent(void) {
     pending_netevent = FALSE;
 
     reentering = 1;
-    i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
+    ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
     reentering = 0;
 
-    if (i < 0) {
-       char buf[1024];
-       switch (WSABASEERR + (-i) % 10000) {
-         case WSAECONNRESET:
-           sprintf(buf, "Connection reset by peer");
-           break;
-         default:
-           sprintf(buf, "Unexpected network error %d", -i);
-           break;
-       }
-        connection_fatal(buf);
-    }
-    if (i <= 0) {
-       if (cfg.close_on_exit)
+    if (ret == 0 && !session_closed) {
+        /* Abnormal exits will already have set session_closed and taken
+         * appropriate action. */
+       if (cfg.close_on_exit == COE_ALWAYS ||
+            cfg.close_on_exit == COE_NORMAL)
            PostQuitMessage(0);
        else {
-           session_closed = TRUE;
-           MessageBox(hwnd, "Connection closed by remote host",
-                      "PuTTY", MB_OK | MB_ICONINFORMATION);
-           SetWindowText (hwnd, "PuTTY (inactive)");
+            session_closed = TRUE;
+            SetWindowText (hwnd, "PuTTY (inactive)");
+            MessageBox(hwnd, "Connection closed by remote host",
+                       "PuTTY", MB_OK | MB_ICONINFORMATION);
        }
     }
 }
@@ -740,7 +822,7 @@ font_messup:
 
     if (cfg.fontisbold) {
        fw_dontcare = FW_BOLD;
-       fw_bold = FW_BLACK;
+       fw_bold = FW_HEAVY;
    } else {
        fw_dontcare = FW_DONTCARE;
        fw_bold = FW_BOLD;
@@ -749,6 +831,9 @@ font_messup:
     hdc = GetDC(hwnd);
 
     font_height = cfg.fontheight;
+    if (font_height > 0) {
+        font_height = -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+    }
     font_width = pick_width;
 
 #define f(i,c,w,u) \
@@ -975,9 +1060,14 @@ void request_resize (int w, int h, int refont) {
                  SWP_NOMOVE | SWP_NOZORDER);
 }
 
-static void click (Mouse_Button b, int x, int y) {
+static void click (Mouse_Button b, int x, int y, int shift, int ctrl) {
     int thistime = GetMessageTime();
 
+    if (send_raw_mouse) {
+       term_mouse(b, MA_CLICK, x, y, shift, ctrl);
+       return;
+    }
+
     if (lastbtn == b && thistime - lasttime < dbltime) {
        lastact = (lastact == MA_CLICK ? MA_2CLK :
                   lastact == MA_2CLK ? MA_3CLK :
@@ -987,10 +1077,34 @@ static void click (Mouse_Button b, int x, int y) {
        lastact = MA_CLICK;
     }
     if (lastact != MA_NOTHING)
-       term_mouse (b, lastact, x, y);
+       term_mouse (b, lastact, x, y, shift, ctrl);
     lasttime = thistime;
 }
 
+/*
+ * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
+ * into a cooked one (SELECT, EXTEND, PASTE).
+ */
+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;
+    if (button == MBT_RIGHT)
+       return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
+}
+
+static void show_mouseptr(int show) {
+    static int cursor_visible = 1;
+    if (!cfg.hide_mouseptr)            /* override if this feature disabled */
+        show = 1;
+    if (cursor_visible && !show)
+        ShowCursor(FALSE);
+    else if (!cursor_visible && show)
+        ShowCursor(TRUE);
+    cursor_visible = show;
+}
+
 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                                  WPARAM wParam, LPARAM lParam) {
     HDC hdc;
@@ -998,6 +1112,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
     static int ignore_clip = FALSE;
     static int just_reconfigged = FALSE;
     static int resizing = FALSE;
+    static int need_backend_resize = FALSE;
 
     switch (message) {
       case WM_TIMER:
@@ -1005,13 +1120,25 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            enact_pending_netevent();
        if (inbuf_head)
            term_out();
+        noise_regular();
         HideCaret(hwnd);
        term_update();
         ShowCaret(hwnd);
+       if (cfg.ping_interval > 0)
+        {
+           time_t now;
+           time(&now);
+           if (now-last_movement > cfg.ping_interval)
+           {
+              back->special(TS_PING);
+              last_movement = now;
+           }
+        }
        return 0;
       case WM_CREATE:
        break;
       case WM_CLOSE:
+        show_mouseptr(1);
        if (!cfg.warn_on_close || session_closed ||
            MessageBox(hwnd, "Are you sure you want to close this session?",
                       "PuTTY Exit Confirmation",
@@ -1019,6 +1146,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            DestroyWindow(hwnd);
        return 0;
       case WM_DESTROY:
+        show_mouseptr(1);
        PostQuitMessage (0);
        return 0;
       case WM_SYSCOMMAND:
@@ -1067,7 +1195,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                    cl = c;
                } else if (wParam == IDM_SAVEDSESS) {
                    char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
-                   cl = malloc(16 + strlen(session)); /* 8, but play safe */
+                   cl = smalloc(16 + strlen(session)); /* 8, but play safe */
                    if (!cl)
                        cl = NULL;     /* not a very important failure mode */
                    else {
@@ -1091,82 +1219,145 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                if (filemap)
                    CloseHandle(filemap);
                if (freecl)
-                   free(cl);
+                   sfree(cl);
            }
            break;
-         case IDM_RECONF:
-           if (!do_reconfig(hwnd))
-               break;
-           just_reconfigged = TRUE;
-           {
-               int i;
-               for (i=0; i<8; i++)
-                   if (fonts[i])
-                       DeleteObject(fonts[i]);
-           }
-           bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
-           und_mode = UND_FONT;
-           init_fonts(0);
-           sfree(logpal);
-           /* Telnet will change local echo -> remote if the remote asks */
-           if (cfg.protocol != PROT_TELNET)
-               ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
-           if (pal)
-               DeleteObject(pal);
-           logpal = NULL;
-           pal = NULL;
-           cfgtopalette();
-           init_palette();
-
-           /* Enable or disable the scroll bar, etc */
-           {
-               LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
+          case IDM_RECONF:
+            {
+                int prev_alwaysontop = cfg.alwaysontop;
+                int prev_sunken_edge = cfg.sunken_edge;
+               char oldlogfile[FILENAME_MAX];
+               int oldlogtype;
+               int need_setwpos = FALSE;
+               int old_fwidth, old_fheight;
+
+               strcpy(oldlogfile, cfg.logfilename);
+               oldlogtype = cfg.logtype;
+               cfg.width = cols;
+               cfg.height = rows;
+               old_fwidth = font_width;
+               old_fheight = font_height;
+                GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
+
+                if (!do_reconfig(hwnd))
+                    break;
+
+               if (strcmp(oldlogfile, cfg.logfilename) ||
+                   oldlogtype != cfg.logtype) {
+                   logfclose();       /* reset logging */
+                   logfopen();
+               }
 
-               nflg = flag;
-               if (cfg.scrollbar) nflg |=  WS_VSCROLL;
-               else               nflg &= ~WS_VSCROLL;
-               if (cfg.locksize) 
-                  nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
-               else              
-                  nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
+                just_reconfigged = TRUE;
+                {
+                    int i;
+                    for (i=0; i<8; i++)
+                        if (fonts[i])
+                            DeleteObject(fonts[i]);
+                }
+                bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
+                und_mode = UND_FONT;
+                init_fonts(0);
+                sfree(logpal);
+                /*
+                 * Flush the line discipline's edit buffer in the
+                 * case where local editing has just been disabled.
+                 */
+                ldisc_send(NULL, 0);
+                if (pal)
+                    DeleteObject(pal);
+                logpal = NULL;
+                pal = NULL;
+                cfgtopalette();
+                init_palette();
+
+                /* Enable or disable the scroll bar, etc */
+                {
+                    LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
+                    LONG nexflag, exflag = GetWindowLong(hwnd, GWL_EXSTYLE);
+
+                    nexflag = exflag;
+                    if (cfg.alwaysontop != prev_alwaysontop) {
+                        if (cfg.alwaysontop) {
+                            nexflag |= WS_EX_TOPMOST;
+                            SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+                                         SWP_NOMOVE | SWP_NOSIZE);
+                        } else {
+                            nexflag &= ~(WS_EX_TOPMOST);
+                            SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+                                         SWP_NOMOVE | SWP_NOSIZE);
+                        }
+                    }
+                    if (cfg.sunken_edge)
+                        nexflag |= WS_EX_CLIENTEDGE;
+                    else
+                        nexflag &= ~(WS_EX_CLIENTEDGE);
+
+                    nflg = flag;
+                    if (cfg.scrollbar) nflg |=  WS_VSCROLL;
+                    else               nflg &= ~WS_VSCROLL;
+                    if (cfg.locksize)
+                        nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
+                    else
+                        nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
+
+                    if (nflg != flag || nexflag != exflag)
+                    {
+                        RECT cr, wr;
+
+                        if (nflg != flag)
+                            SetWindowLong(hwnd, GWL_STYLE, nflg);
+                        if (nexflag != exflag)
+                            SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
+
+                        SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
+
+                        SetWindowPos(hwnd, NULL, 0,0,0,0,
+                                     SWP_NOACTIVATE|SWP_NOCOPYBITS|
+                                     SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|
+                                     SWP_FRAMECHANGED);
+
+                        GetWindowRect (hwnd, &wr);
+                        GetClientRect (hwnd, &cr);
+                        extra_width = wr.right - wr.left - cr.right + cr.left;
+                        extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
+                    }
+                }
 
-               if (nflg != flag)
-               {
-                   RECT cr, wr;
-
-                   SetWindowLong(hwnd, GWL_STYLE, nflg);
-                   SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
-                   SetWindowPos(hwnd, NULL, 0,0,0,0,
-                        SWP_NOACTIVATE|SWP_NOCOPYBITS|
-                        SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
-                        SWP_FRAMECHANGED);
-
-                   GetWindowRect (hwnd, &wr);
-                   GetClientRect (hwnd, &cr);
-                   extra_width = wr.right - wr.left - cr.right + cr.left;
-                   extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
+               if (cfg.height != rows ||
+                   cfg.width != cols ||
+                   old_fwidth != font_width ||
+                   old_fheight != font_height ||
+                   cfg.savelines != savelines ||
+                    cfg.sunken_edge != prev_sunken_edge)
+                   need_setwpos = TRUE;
+                term_size(cfg.height, cfg.width, cfg.savelines);
+                InvalidateRect(hwnd, NULL, TRUE);
+                if (need_setwpos) {
+                   force_normal(hwnd);
+                   SetWindowPos (hwnd, NULL, 0, 0,
+                                 extra_width + font_width * cfg.width,
+                                 extra_height + font_height * cfg.height,
+                                 SWP_NOACTIVATE | SWP_NOCOPYBITS |
+                                 SWP_NOMOVE | SWP_NOZORDER);
                }
-           }
-
-           term_size(cfg.height, cfg.width, cfg.savelines);
-           InvalidateRect(hwnd, NULL, TRUE);
-           SetWindowPos (hwnd, NULL, 0, 0,
-                         extra_width + font_width * cfg.width,
-                         extra_height + font_height * cfg.height,
-                         SWP_NOACTIVATE | SWP_NOCOPYBITS |
-                         SWP_NOMOVE | SWP_NOZORDER);
-           if (IsIconic(hwnd)) {
-               SetWindowText (hwnd,
-                              cfg.win_name_always ? window_name : icon_name);
-           }
-           break;
-         case IDM_CLRSB:
-           term_clrsb();
-           break;
-         case IDM_RESET:
-           term_pwron();
+                set_title(cfg.wintitle);
+                if (IsIconic(hwnd)) {
+                    SetWindowText (hwnd,
+                                   cfg.win_name_always ? window_name : icon_name);
+                }
+            }
+            break;
+         case IDM_COPYALL:
+           term_copyall();
            break;
-         case IDM_TEL_AYT: back->special (TS_AYT); break;
+          case IDM_CLRSB:
+            term_clrsb();
+            break;
+          case IDM_RESET:
+            term_pwron();
+            break;
+          case IDM_TEL_AYT: back->special (TS_AYT); break;
          case IDM_TEL_BRK: back->special (TS_BRK); break;
          case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
          case IDM_TEL_EC: back->special (TS_EC); break;
@@ -1194,61 +1385,99 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 
 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
+#define WHEEL_DELTA 120
+      case WM_MOUSEWHEEL:
+       {
+           wheel_accumulator += (short)HIWORD(wParam);
+           wParam = LOWORD(wParam);
+
+           /* process events when the threshold is reached */
+           while (abs(wheel_accumulator) >= WHEEL_DELTA) {
+               int b;
 
+               /* reduce amount for next time */
+               if (wheel_accumulator > 0) {
+                   b = MBT_WHEEL_UP;
+                   wheel_accumulator -= WHEEL_DELTA;
+               }
+               else if (wheel_accumulator < 0) {
+                   b = MBT_WHEEL_DOWN;
+                   wheel_accumulator += WHEEL_DELTA;
+               }
+               else
+                   break;
+
+               if (send_raw_mouse) {
+                   /* send a mouse-down followed by a mouse up */
+                   term_mouse(b,
+                              MA_CLICK,
+                              TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
+                              wParam & MK_SHIFT, wParam & MK_CONTROL);
+                   term_mouse(b,
+                              MA_RELEASE,
+                              TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
+                              wParam & MK_SHIFT, wParam & MK_CONTROL);
+               } else {
+                   /* trigger a scroll */
+                   term_scroll(0, b == MBT_WHEEL_UP ? -rows/2 : rows/2);
+               }
+           }
+           return 0;
+       }
       case WM_LBUTTONDOWN:
-       click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
-              TO_CHR_Y(Y_POS(lParam)));
-        SetCapture(hwnd);
-       return 0;
-      case WM_LBUTTONUP:
-       term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
-                   TO_CHR_Y(Y_POS(lParam)));
-        ReleaseCapture();
-       return 0;
       case WM_MBUTTONDOWN:
-        SetCapture(hwnd);
-       click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
-              TO_CHR_X(X_POS(lParam)),
-              TO_CHR_Y(Y_POS(lParam)));
-       return 0;
-      case WM_MBUTTONUP:
-       term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
-                   MA_RELEASE, TO_CHR_X(X_POS(lParam)),
-                   TO_CHR_Y(Y_POS(lParam)));
-        ReleaseCapture();
-       return 0;
       case WM_RBUTTONDOWN:
-        SetCapture(hwnd);
-       click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
-              TO_CHR_X(X_POS(lParam)),
-              TO_CHR_Y(Y_POS(lParam)));
-       return 0;
+      case WM_LBUTTONUP:
+      case WM_MBUTTONUP:
       case WM_RBUTTONUP:
-       term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
-                   MA_RELEASE, TO_CHR_X(X_POS(lParam)),
-                   TO_CHR_Y(Y_POS(lParam)));
-        ReleaseCapture();
+       {
+           int button, press;
+           switch (message) {
+             case WM_LBUTTONDOWN: button = MBT_LEFT;   press = 1; break;
+             case WM_MBUTTONDOWN: button = MBT_MIDDLE; press = 1; break;
+             case WM_RBUTTONDOWN: button = MBT_RIGHT;  press = 1; break;
+             case WM_LBUTTONUP:   button = MBT_LEFT;   press = 0; break;
+             case WM_MBUTTONUP:   button = MBT_MIDDLE; press = 0; break;
+             case WM_RBUTTONUP:   button = MBT_RIGHT;  press = 0; break;
+           }
+           show_mouseptr(1);
+           if (press) {
+               click (button,
+                      TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
+                      wParam & MK_SHIFT, wParam & MK_CONTROL);
+               SetCapture(hwnd);
+           } else {
+               term_mouse (button, MA_RELEASE,
+                           TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
+                           wParam & MK_SHIFT, wParam & MK_CONTROL);
+               ReleaseCapture();
+           }
+       }
        return 0;
       case WM_MOUSEMOVE:
+        show_mouseptr(1);
        /*
         * Add the mouse position and message time to the random
-        * number noise, if we're using ssh.
+        * number noise.
         */
-       if (cfg.protocol == PROT_SSH)
-           noise_ultralight(lParam);
+        noise_ultralight(lParam);
 
        if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
            Mouse_Button b;
            if (wParam & MK_LBUTTON)
-               b = MB_SELECT;
+               b = MBT_SELECT;
            else if (wParam & MK_MBUTTON)
-               b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
+               b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
            else
-               b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
+               b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
            term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
-                       TO_CHR_Y(Y_POS(lParam)));
+                       TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL);
        }
        return 0;
+      case WM_NCMOUSEMOVE:
+       show_mouseptr(1);
+        noise_ultralight(lParam);
+       return 0;
       case WM_IGNORE_CLIP:
        ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
        break;
@@ -1285,15 +1514,18 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        pending_netevent = TRUE;
        pend_netevent_wParam=wParam;
        pend_netevent_lParam=lParam;
+       time(&last_movement);
        return 0;
       case WM_SETFOCUS:
        has_focus = TRUE;
-        CreateCaret(hwnd, caretbm, 0, 0);
+        CreateCaret(hwnd, caretbm, font_width, font_height);
         ShowCaret(hwnd);
+        compose_state = 0;
        term_out();
        term_update();
        break;
       case WM_KILLFOCUS:
+        show_mouseptr(1);
        has_focus = FALSE;
         DestroyCaret();
        term_out();
@@ -1305,11 +1537,13 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
       case WM_ENTERSIZEMOVE:
         EnableSizeTip(1);
         resizing = TRUE;
+       need_backend_resize = FALSE;
         break;
       case WM_EXITSIZEMOVE:
         EnableSizeTip(0);
         resizing = FALSE;
-        back->size();
+       if (need_backend_resize)
+           back->size();
         break;
       case WM_SIZING:
        {
@@ -1385,6 +1619,8 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                  */
                 if (!resizing)
                     back->size();
+               else
+                   need_backend_resize = TRUE;
                just_reconfigged = FALSE;
            }
        }
@@ -1429,10 +1665,9 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
       case WM_SYSKEYUP:
        /*
         * Add the scan code and keypress timing to the random
-        * number noise, if we're using ssh.
+        * number noise.
         */
-       if (cfg.protocol == PROT_SSH)
-           noise_ultralight(lParam);
+        noise_ultralight(lParam);
 
        /*
         * We don't do TranslateMessage since it disassociates the
@@ -1445,12 +1680,32 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
            unsigned char buf[20];
            int len;
 
-           len = TranslateKey (message, wParam, lParam, buf);
-           if (len == -1)
-               return DefWindowProc (hwnd, message, wParam, lParam);
-           ldisc->send (buf, len);
+            if (wParam==VK_PROCESSKEY) {
+               MSG m;
+                m.hwnd = hwnd;
+                m.message = WM_KEYDOWN;
+                m.wParam = wParam;
+                m.lParam = lParam & 0xdfff;
+                TranslateMessage(&m);
+            } else {
+               len = TranslateKey (message, wParam, lParam, buf);
+               if (len == -1)
+                   return DefWindowProc (hwnd, message, wParam, lParam);
+               ldisc_send (buf, len);
+
+                if (len > 0)
+                    show_mouseptr(0);
+           }
        }
        return 0;
+      case WM_IME_CHAR:
+       {
+           unsigned char buf[2];
+
+           buf[1] = wParam;
+           buf[0] = wParam >> 8;
+           ldisc_send (buf, 2);
+       }
       case WM_CHAR:
       case WM_SYSCHAR:
        /*
@@ -1459,11 +1714,16 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
         * post the things to us as part of a macro manoeuvre,
         * we're ready to cope.
         */
-       {
-           char c = xlat_kbd2tty((unsigned char)wParam);
-           ldisc->send (&c, 1);
+               {
+                   char c = xlat_kbd2tty((unsigned char)wParam);
+                   ldisc_send (&c, 1);
        }
        return 0;
+               case WM_SETCURSOR:
+               if (send_raw_mouse) {
+                   SetCursor(LoadCursor(NULL, IDC_ARROW));
+                   return TRUE;
+               }
     }
 
     return DefWindowProc (hwnd, message, wParam, lParam);
@@ -1476,7 +1736,8 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
  * have one.)
  */
 void sys_cursor(int x, int y) {
-    SetCaretPos(x * font_width, y * font_height);
+    if (has_focus)
+       SetCaretPos(x * font_width, y * font_height);
 }
 
 /*
@@ -1509,7 +1770,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
     x *= fnt_width;
     y *= font_height;
 
-    if (attr & ATTR_ACTCURS) {
+    if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
        attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
        attr ^= ATTR_CUR_XOR;
     }
@@ -1546,7 +1807,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
 #endif
                /* This is CP437 ... junk translation */
                static const unsigned char oemhighhalf[] = {
-                   0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
+                   0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
                    0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
                    0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
                    0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
@@ -1564,16 +1825,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
            }
     }
 
-    if (attr & ATTR_GBCHR) {
-       int i;
-       /*
-        * GB mapping: map # to pound, and everything else stays
-        * normal.
-        */
-       for (i=0; i<len; i++)
-           if (text[i] == '#')
-               text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
-    } else if (attr & ATTR_LINEDRW) {
+    if (attr & ATTR_LINEDRW) {
        int i;
        /* ISO 8859-1 */
        static const char poorman[] =
@@ -1597,6 +1849,8 @@ void do_text (Context ctx, int x, int y, char *text, int len,
        /*
         * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
         * VT100 line drawing chars; everything else stays normal.
+        *
+        * Actually '_' maps to space too, but that's done before.
         */
        switch (cfg.vtmode) {
          case VT_XWINDOWS:
@@ -1677,7 +1931,7 @@ void do_text (Context ctx, int x, int y, char *text, int len,
         oldpen = SelectObject (hdc, oldpen);
         DeleteObject (oldpen);
     }
-    if (attr & ATTR_PASCURS) {
+    if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
        POINT pts[5];
         HPEN oldpen;
        pts[0].x = pts[1].x = pts[4].x = x;
@@ -1689,6 +1943,34 @@ void do_text (Context ctx, int x, int y, char *text, int len,
         oldpen = SelectObject (hdc, oldpen);
         DeleteObject (oldpen);
     }
+    if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
+        int startx, starty, dx, dy, length, i;
+       if (cfg.cursor_type == 1) {
+            startx = x; starty = y+descent;
+            dx = 1; dy = 0; length = fnt_width;
+        } else {
+           int xadjust = 0;
+           if (attr & ATTR_RIGHTCURS)
+               xadjust = fnt_width-1;
+            startx = x+xadjust; starty = y;
+            dx = 0; dy = 1; length = font_height;
+       }
+        if (attr & ATTR_ACTCURS) {
+            HPEN oldpen;
+            oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
+            MoveToEx (hdc, startx, starty, NULL);
+            LineTo (hdc, startx+dx*length, starty+dy*length);
+            oldpen = SelectObject (hdc, oldpen);
+            DeleteObject (oldpen);
+        } else {
+            for (i = 0; i < length; i++) {
+                if (i % 2 == 0) {
+                    SetPixel(hdc, startx, starty, colours[23]);
+                }
+                startx += dx; starty += dy;
+            }
+        }
+    }
 }
 
 static int check_compose(int first, int second) {
@@ -1712,15 +1994,6 @@ static int check_compose(int first, int second) {
     static int recurse = 0;
     int nc = -1;
 
-    if(0)
-    {
-       char buf[256];
-       char * p;
-       sprintf(buf, "cc(%d,%d)", first, second);
-       for(p=buf; *p; p++)
-           c_write1(*p);
-    }
-
     for(c=composetbl; *c; c++) {
        if( (*c)[0] == first && (*c)[1] == second)
        {
@@ -1747,44 +2020,89 @@ static int check_compose(int first, int second) {
  * codes. Returns number of bytes used or zero to drop the message
  * or -1 to forward the message to windows.
  */
-static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
+static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
+                       unsigned char *output) {
     BYTE keystate[256];
     int  scan, left_alt = 0, key_down, shift_state;
     int  r, i, code;
     unsigned char * p = output;
+    static int alt_state = 0;
 
-static WORD keys[3];
-static int compose_state = 0;
-static int compose_char = 0;
-static WPARAM compose_key = 0;
+    HKL kbd_layout = GetKeyboardLayout(0);
 
+    static WORD keys[3];
+    static int compose_char = 0;
+    static WPARAM compose_key = 0;
+    
     r = GetKeyboardState(keystate);
     if (!r) memset(keystate, 0, sizeof(keystate));
     else
     {
-       /* Note if AltGr was pressed and if it was used as a compose key */
-       if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
-       {
+#if 0
+       {  /* Tell us all about key events */
+         static BYTE oldstate[256];
+         static int first = 1;
+         static int scan;
+         int ch;
+         if(first) memcpy(oldstate, keystate, sizeof(oldstate));
+         first=0;
+
+         if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
+            debug(("+"));
+         } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
+            debug((". U"));
+         } else {
+            debug((".\n"));
+            if (wParam >= VK_F1 && wParam <= VK_F20 )
+               debug(("K_F%d", wParam+1-VK_F1));
+            else switch(wParam)
+            {
+            case VK_SHIFT:   debug(("SHIFT")); break;
+            case VK_CONTROL: debug(("CTRL")); break;
+            case VK_MENU:    debug(("ALT")); break;
+            default:         debug(("VK_%02x", wParam));
+            }
+            if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
+               debug(("*"));
+            debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
+
+            ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
+            if (ch>=' ' && ch<='~') debug((", '%c'", ch));
+            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 ( (keystate[VK_SHIFT]&0x80)!=0)     debug((", S"));
+            if ( (keystate[VK_CONTROL]&0x80)!=0)   debug((", C"));
+            if ( (HIWORD(lParam)&KF_EXTENDED) )    debug((", E"));
+            if ( (HIWORD(lParam)&KF_UP) )          debug((", U"));
+         }
+
+         if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
+            ;
+         else if ( (HIWORD(lParam)&KF_UP) ) 
+            oldstate[wParam&0xFF] ^= 0x80;
+         else
+            oldstate[wParam&0xFF] ^= 0x81;
+
+         for(ch=0; ch<256; ch++)
+            if (oldstate[ch] != keystate[ch])
+               debug((", M%02x=%02x", ch, keystate[ch]));
+
+         memcpy(oldstate, keystate, sizeof(oldstate));
+       }
+#endif
+
+       if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
            keystate[VK_RMENU] = keystate[VK_MENU];
-           if (!compose_state) compose_key = wParam;
        }
-       if (wParam == VK_APPS && !compose_state)
-           compose_key = wParam;
 
-       if (wParam == compose_key)
-       {
-            if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
-              compose_state = 1;
-           else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
-              compose_state = 2;
-           else
-              compose_state = 0;
-       }
-       else if (compose_state==1 && wParam != VK_CONTROL)
-          compose_state = 0;
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
-       if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
+       if ( (cfg.funky_type == 3 ||
+              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
              && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
 
            wParam = VK_EXECUTE;
@@ -1807,14 +2125,43 @@ static WPARAM compose_key = 0;
 
     key_down = ((HIWORD(lParam)&KF_UP)==0);
 
-    /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
-    if (left_alt && (keystate[VK_CONTROL]&0x80))
-       keystate[VK_MENU] = 0;
+    /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
+    if (left_alt && (keystate[VK_CONTROL]&0x80)) {
+       if (cfg.ctrlaltkeys)
+           keystate[VK_MENU] = 0;
+       else {
+           keystate[VK_RMENU] = 0x80;
+           left_alt = 0;
+       }
+    }
 
     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
     shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
                 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
 
+    /* Note if AltGr was pressed and if it was used as a compose key */
+    if (!compose_state) {
+       compose_key = 0x100;
+       if (cfg.compose_key) {
+           if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
+               compose_key = wParam;
+       }
+       if (wParam == VK_APPS)
+           compose_key = wParam;
+    }
+
+    if (wParam == compose_key)
+    {
+       if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
+           compose_state = 1;
+       else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
+           compose_state = 2;
+       else
+           compose_state = 0;
+    }
+    else if (compose_state==1 && wParam != VK_CONTROL)
+       compose_state = 0;
+
     /* 
      * Record that we pressed key so the scroll window can be reset, but
      * be careful to avoid Shift-UP/Down
@@ -1829,8 +2176,9 @@ static WPARAM compose_key = 0;
     if (compose_state>1 && left_alt) compose_state = 0;
 
     /* Sanitize the number pad if not using a PC NumPad */
-    if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
-         || cfg.nethack_keypad || compose_state )
+    if( left_alt || (app_keypad_keys && !cfg.no_applic_k
+                     && cfg.funky_type != 2)
+       || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
     {
        if ((HIWORD(lParam)&KF_EXTENDED) == 0)
        {
@@ -1873,18 +2221,24 @@ static WPARAM compose_key = 0;
             return 0;
        }
         if (wParam == VK_INSERT && shift_state == 1) {
-            term_mouse (MB_PASTE, MA_CLICK, 0, 0);
-            term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
+            term_mouse (MBT_PASTE, MA_CLICK, 0, 0, 0, 0);
+            term_mouse (MBT_PASTE, MA_RELEASE, 0, 0, 0, 0);
             return 0;
         }
        if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
             return -1;
        }
        if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
-            
+           alt_state = 0;
+            PostMessage(hwnd, WM_CHAR, ' ', 0);
             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
             return -1;
        }
+       /* Control-Numlock for app-keypad mode switch */
+       if (wParam == VK_PAUSE && shift_state == 2) {
+           app_keypad_keys ^= 1;
+           return 0;
+       }
 
        /* Nethack keypad */
        if (cfg.nethack_keypad && !left_alt) {
@@ -1905,14 +2259,15 @@ static WPARAM compose_key = 0;
        if (!left_alt) {
           int xkey = 0;
 
-          if ( cfg.funky_type == 0 ||
-             ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
-              case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
+          if ( cfg.funky_type == 3 ||
+             ( cfg.funky_type <= 1 &&
+               app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
+              case VK_EXECUTE: xkey = 'P'; break;
               case VK_DIVIDE:  xkey = 'Q'; break;
               case VK_MULTIPLY:xkey = 'R'; break;
               case VK_SUBTRACT:xkey = 'S'; break;
           }
-          if(app_keypad_keys) switch(wParam) {
+          if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
               case VK_NUMPAD0: xkey = 'p'; break;
               case VK_NUMPAD1: xkey = 'q'; break;
               case VK_NUMPAD2: xkey = 'r'; break;
@@ -1925,9 +2280,17 @@ static WPARAM compose_key = 0;
               case VK_NUMPAD9: xkey = 'y'; break;
 
               case VK_DECIMAL: xkey = 'n'; break;
-              case VK_ADD:     if(shift_state) xkey = 'm'; 
-                               else            xkey = 'l';
+              case VK_ADD:     if(cfg.funky_type==2) { 
+                                   if(shift_state) xkey = 'l';
+                                   else            xkey = 'k';
+                               } else if(shift_state)  xkey = 'm'; 
+                                 else                  xkey = 'l';
                                break;
+
+              case VK_DIVIDE:  if(cfg.funky_type==2) xkey = 'o'; break;
+              case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
+              case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
+
               case VK_RETURN:
                                if (HIWORD(lParam)&KF_EXTENDED)
                                    xkey = 'M';
@@ -1969,6 +2332,10 @@ static WPARAM compose_key = 0;
        {
            *p++ = 3; return p - output;
        }
+       if (wParam == VK_PAUSE)                         /* Break/Pause */
+       {
+           *p++ = 26; *p++ = 0; return -2;
+       }
        /* Control-2 to Control-8 are special */
        if (shift_state == 2 && wParam >= '2' && wParam <= '8')
        {
@@ -2026,12 +2393,36 @@ static WPARAM compose_key = 0;
          case VK_PRIOR: code = 5; break;
          case VK_NEXT: code = 6; break;
        }
+       /* Reorder edit keys to physical order */
+       if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
+
+       if (vt52_mode && code > 0 && code <= 6) {
+           p += sprintf((char *)p, "\x1B%c", " HLMEIG"[code]);
+           return p - output;
+       }
+
+       if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
+           p += sprintf((char *)p, "\x1B[%c", code + 'M' - 11);
+           return p - output;
+       }
+       if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
+           int offt = 0;
+           if (code>15) offt++; if (code>21) offt++;
+           if (vt52_mode)
+               p += sprintf((char *)p, "\x1B%c", code + 'P' - 11 - offt);
+           else
+               p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11 - offt);
+           return p - output;
+       }
        if (cfg.funky_type == 1 && 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) {
-           p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
+           if (vt52_mode)
+               p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
+           else
+               p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
            return p - output;
        }
        if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
@@ -2060,13 +2451,23 @@ static WPARAM compose_key = 0;
            {
                if (vt52_mode)
                    p += sprintf((char *)p, "\x1B%c", xkey);
-               else if (app_cursor_keys)
+               else if (app_cursor_keys && !cfg.no_applic_c)
                    p += sprintf((char *)p, "\x1BO%c", xkey);
                else
                    p += sprintf((char *)p, "\x1B[%c", xkey);
                return p - output;
            }
        }
+
+       /*
+        * Finally, deal with Return ourselves. (Win95 seems to
+        * foul it up when Alt is pressed, for some reason.)
+        */
+       if (wParam == VK_RETURN)       /* Return */
+       {
+           *p++ = 0x0D;
+           return p-output;
+       }
     }
 
     /* Okay we've done everything interesting; let windows deal with 
@@ -2078,7 +2479,7 @@ static WPARAM compose_key = 0;
        if(cfg.xlat_capslockcyr)
            keystate[VK_CAPITAL] = 0;
 
-       r = ToAscii (wParam, scan, keystate, keys, 0);
+       r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
        if(r>0)
        {
            p = output;
@@ -2097,7 +2498,7 @@ static WPARAM compose_key = 0;
 
                    if ((nc=check_compose(compose_char,ch)) == -1)
                    {
-                       c_write1('\007');
+                       MessageBeep(MB_ICONHAND);
                        return 0;
                    }
                    *p++ = xlat_kbd2tty((unsigned char)nc);
@@ -2122,14 +2523,23 @@ static WPARAM compose_key = 0;
 
            return p-output;
        }
+       /* If we're definitly not building up an ALT-54321 then clear it */
+       if (!left_alt) keys[0] = 0;
     }
 
-    /* This stops ALT press-release doing a 'COMMAND MENU' function */
-    if (message == WM_SYSKEYUP && wParam == VK_MENU) 
-    {
-       keystate[VK_MENU] = 0;
-       return 0;
+    /* ALT alone may or may not want to bring up the System menu */
+    if (wParam == VK_MENU) {
+        if (cfg.alt_only) {
+            if (message == WM_SYSKEYDOWN)
+                alt_state = 1;
+            else if (message == WM_SYSKEYUP && alt_state)
+                PostMessage(hwnd, WM_CHAR, ' ', 0);
+            if (message == WM_SYSKEYUP)
+                alt_state = 0;
+        } else
+           return 0;
     }
+    else alt_state = 0;
 
     return -1;
 }
@@ -2237,7 +2647,7 @@ void palette_reset (void) {
     }
 }
 
-void write_clip (void *data, int len) {
+void write_clip (void *data, int len, int must_deselect) {
     HGLOBAL clipdata;
     void *lock;
 
@@ -2251,14 +2661,18 @@ void write_clip (void *data, int len) {
     ((unsigned char *) lock) [len] = 0;
     GlobalUnlock (clipdata);
 
-    SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
+    if (!must_deselect)
+        SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
+
     if (OpenClipboard (hwnd)) {
        EmptyClipboard();
        SetClipboardData (CF_TEXT, clipdata);
        CloseClipboard();
     } else
        GlobalFree (clipdata);
-    SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
+
+    if (!must_deselect)
+        SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
 }
 
 void get_clip (void **p, int *len) {
@@ -2320,21 +2734,30 @@ void fatalbox(char *fmt, ...) {
 /*
  * Beep.
  */
-void beep(int errorbeep) {
-    static long last_beep = 0;
-    long now, beep_diff;
-
-    now = GetTickCount();
-    beep_diff = now-last_beep;
-
-    /* Make sure we only respond to one beep per packet or so */
-    if (beep_diff>=0 && beep_diff<50)
-        return;
-
-    if(errorbeep)
-       MessageBeep(MB_ICONHAND);
-    else
-       MessageBeep(MB_OK);
-
-    last_beep = GetTickCount();
+void beep(int mode) {
+    if (mode == BELL_DEFAULT) {
+       /*
+        * For MessageBeep style bells, we want to be careful of
+        * timing, because they don't have the nice property of
+        * PlaySound bells that each one cancels the previous
+        * active one. So we limit the rate to one per 50ms or so.
+        */
+       static long lastbeep = 0;
+       long now, beepdiff;
+
+       now = GetTickCount();
+       beepdiff = now - lastbeep;
+       if (beepdiff >= 0 && beepdiff < 50)
+           return;
+       MessageBeep(MB_OK);
+       lastbeep = now;
+    } else if (mode == BELL_WAVEFILE) {
+       if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
+           char buf[sizeof(cfg.bell_wavefile)+80];
+           sprintf(buf, "Unable to play sound file\n%s\n"
+                   "Using default sound instead", cfg.bell_wavefile);
+           MessageBox(hwnd, buf, "PuTTY Sound Error", MB_OK | MB_ICONEXCLAMATION);
+           cfg.beep = BELL_DEFAULT;
+       }
+    }
 }