Wez Furlong's patch to add xterm mouse reporting and proper mouse
[u/mdw/putty] / window.c
index f810eba..6f3eb49 100644 (file)
--- a/window.c
+++ b/window.c
@@ -1,6 +1,7 @@
 #include <windows.h>
 #include <imm.h>
 #include <commctrl.h>
+#include <mmsystem.h>
 #ifndef AUTO_WINSOCK
 #ifdef WINSOCK_TWO
 #include <winsock2.h>
@@ -42,6 +43,9 @@
 #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
 
@@ -99,15 +103,16 @@ static HBITMAP caretbm;
 static int dbltime, lasttime, lastact;
 static Mouse_Button lastbtn;
 
-static char *window_name, *icon_name;
+/* this allows xterm-style mouse handling. */
+static int send_raw_mouse = 0;
+static int wheel_accumulator = 0;
 
-static Ldisc *real_ldisc;
+static char *window_name, *icon_name;
 
 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";
@@ -149,6 +154,7 @@ 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, &cfg);
 
@@ -169,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' &&
@@ -322,11 +323,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         return 1;
     }
 
-    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;
-
     if (!prev) {
        wndclass.style         = 0;
        wndclass.lpfnWndProc   = WndProc;
@@ -379,7 +375,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         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.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,
@@ -453,7 +450,9 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
 
        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;
        }
@@ -480,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();
 
     /*
@@ -537,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;
@@ -563,7 +567,8 @@ 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);
 
            /* Make sure we blink everything that needs it. */
            term_blink(0);
@@ -601,14 +606,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
                term_out();
            term_update();
             ShowCaret(hwnd);
-           if (!has_focus)
-              timer_id = SetTimer(hwnd, 1, 59500, NULL);
+           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 apperently unneccesary wait here 
+            * so we do an apparently unnecessary wait here
             */
            WaitMessage();
            if (GetMessage (&msg, NULL, 0, 0) != 1)
@@ -663,6 +671,15 @@ char *do_select(SOCKET skt, int startup) {
 }
 
 /*
+ * 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, ...) {
@@ -673,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;
@@ -698,14 +715,17 @@ static void enact_pending_netevent(void) {
     ret = select_result (pend_netevent_wParam, pend_netevent_lParam);
     reentering = 0;
 
-    if (ret == 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);
        }
     }
 }
@@ -811,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) \
@@ -1037,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 :
@@ -1049,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;
@@ -1076,7 +1128,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
         {
            time_t now;
            time(&now);
-           if (now-last_movement > cfg.ping_interval * 60 - 10)
+           if (now-last_movement > cfg.ping_interval)
            {
               back->special(TS_PING);
               last_movement = now;
@@ -1086,6 +1138,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
       case WM_CREATE:
        break;
       case WM_CLOSE:
+        show_mouseptr(1);
        if (!cfg.warn_on_close || session_closed ||
            MessageBox(hwnd, "Are you sure you want to close this session?",
                       "PuTTY Exit Confirmation",
@@ -1093,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:
@@ -1171,14 +1225,29 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
           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();
+               }
+
                 just_reconfigged = TRUE;
                 {
                     int i;
@@ -1191,11 +1260,10 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                 init_fonts(0);
                 sfree(logpal);
                 /*
-                 * Telnet will change local echo -> remote if the
-                 * remote asks.
+                 * Flush the line discipline's edit buffer in the
+                 * case where local editing has just been disabled.
                  */
-                if (cfg.protocol != PROT_TELNET)
-                    ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
+                ldisc_send(NULL, 0);
                 if (pal)
                     DeleteObject(pal);
                 logpal = NULL;
@@ -1211,15 +1279,19 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                     nexflag = exflag;
                     if (cfg.alwaysontop != prev_alwaysontop) {
                         if (cfg.alwaysontop) {
-                            nexflag = WS_EX_TOPMOST;
+                            nexflag |= WS_EX_TOPMOST;
                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
                                          SWP_NOMOVE | SWP_NOSIZE);
                         } else {
-                            nexflag = 0;
+                            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;
@@ -1256,7 +1328,8 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                    cfg.width != cols ||
                    old_fwidth != font_width ||
                    old_fheight != font_height ||
-                   cfg.savelines != savelines)
+                   cfg.savelines != savelines ||
+                    cfg.sunken_edge != prev_sunken_edge)
                    need_setwpos = TRUE;
                 term_size(cfg.height, cfg.width, cfg.savelines);
                 InvalidateRect(hwnd, NULL, TRUE);
@@ -1268,6 +1341,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                                  SWP_NOACTIVATE | SWP_NOCOPYBITS |
                                  SWP_NOMOVE | SWP_NOZORDER);
                }
+                set_title(cfg.wintitle);
                 if (IsIconic(hwnd)) {
                     SetWindowText (hwnd,
                                    cfg.win_name_always ? window_name : icon_name);
@@ -1311,42 +1385,77 @@ 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.
@@ -1356,15 +1465,19 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        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;
@@ -1412,6 +1525,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        term_update();
        break;
       case WM_KILLFOCUS:
+        show_mouseptr(1);
        has_focus = FALSE;
         DestroyCaret();
        term_out();
@@ -1577,7 +1691,10 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                len = TranslateKey (message, wParam, lParam, buf);
                if (len == -1)
                    return DefWindowProc (hwnd, message, wParam, lParam);
-               ldisc->send (buf, len);
+               ldisc_send (buf, len);
+
+                if (len > 0)
+                    show_mouseptr(0);
            }
        }
        return 0;
@@ -1587,7 +1704,7 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
 
            buf[1] = wParam;
            buf[0] = wParam >> 8;
-           ldisc->send (buf, 2);
+           ldisc_send (buf, 2);
        }
       case WM_CHAR:
       case WM_SYSCHAR:
@@ -1597,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);
@@ -1614,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);
 }
 
 /*
@@ -1647,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;
     }
@@ -1684,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,
@@ -1808,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;
@@ -1820,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) {
@@ -1875,11 +2026,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
     int  scan, left_alt = 0, key_down, shift_state;
     int  r, i, code;
     unsigned char * p = output;
+    static int alt_state = 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
@@ -1912,7 +2066,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                debug(("*"));
             debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
 
-            ch = MapVirtualKey(wParam, 2);
+            ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
             if (ch>=' ' && ch<='~') debug((", '%c'", ch));
             else if (ch)            debug((", $%02x", ch));
 
@@ -1945,29 +2099,6 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            keystate[VK_RMENU] = keystate[VK_MENU];
        }
 
-       /* Note if AltGr was pressed and if it was used as a compose key */
-       if (cfg.compose_key) {
-           if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
-           {
-               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;
-       } else {
-           compose_state = 0;
-       }
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
        if ( (cfg.funky_type == 3 ||
@@ -1994,14 +2125,43 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
     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
@@ -2061,14 +2221,15 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
             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;
@@ -2171,6 +2332,10 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        {
            *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')
        {
@@ -2231,6 +2396,24 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        /* 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;
@@ -2296,7 +2479,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        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;
@@ -2340,12 +2523,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 
            return p-output;
        }
+       /* If we're definitly not building up an ALT-54321 then clear it */
+       if (!left_alt) keys[0] = 0;
     }
 
     /* ALT alone may or may not want to bring up the System menu */
     if (wParam == VK_MENU) {
         if (cfg.alt_only) {
-            static int alt_state = 0;
             if (message == WM_SYSKEYDOWN)
                 alt_state = 1;
             else if (message == WM_SYSKEYUP && alt_state)
@@ -2355,6 +2539,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
         } else
            return 0;
     }
+    else alt_state = 0;
 
     return -1;
 }
@@ -2549,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;
+       }
+    }
 }