Robert de Bath's multi-purpose patch, slightly modified.
[u/mdw/putty] / window.c
index 098f39e..fd46be4 100644 (file)
--- a/window.c
+++ b/window.c
@@ -3,31 +3,36 @@
 #include <winsock.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <ctype.h>
 
 #define PUTTY_DO_GLOBALS                      /* actually _define_ globals */
 #include "putty.h"
 #include "win_res.h"
 
-#define IDM_SHOWLOG   501
-#define IDM_NEWSESS   502
-#define IDM_DUPSESS   503
-#define IDM_RECONF    504
-#define IDM_CLRSB     505
-#define IDM_RESET     506
-#define IDM_TEL_AYT   507
-#define IDM_TEL_BRK   508
-#define IDM_TEL_SYNCH 509
-#define IDM_TEL_EC    510
-#define IDM_TEL_EL    511
-#define IDM_TEL_GA    512
-#define IDM_TEL_NOP   513
-#define IDM_TEL_ABORT 514
-#define IDM_TEL_AO    515
-#define IDM_TEL_IP    516
-#define IDM_TEL_SUSP  517
-#define IDM_TEL_EOR   518
-#define IDM_TEL_EOF   519
-#define IDM_ABOUT     520
+#define IDM_SHOWLOG   0x0010
+#define IDM_NEWSESS   0x0020
+#define IDM_DUPSESS   0x0030
+#define IDM_RECONF    0x0040
+#define IDM_CLRSB     0x0050
+#define IDM_RESET     0x0060
+#define IDM_TEL_AYT   0x0070
+#define IDM_TEL_BRK   0x0080
+#define IDM_TEL_SYNCH 0x0090
+#define IDM_TEL_EC    0x00a0
+#define IDM_TEL_EL    0x00b0
+#define IDM_TEL_GA    0x00c0
+#define IDM_TEL_NOP   0x00d0
+#define IDM_TEL_ABORT 0x00e0
+#define IDM_TEL_AO    0x00f0
+#define IDM_TEL_IP    0x0100
+#define IDM_TEL_SUSP  0x0110
+#define IDM_TEL_EOR   0x0120
+#define IDM_TEL_EOF   0x0130
+#define IDM_ABOUT     0x0140
+#define IDM_SAVEDSESS 0x0150
+
+#define IDM_SAVED_MIN 0x1000
+#define IDM_SAVED_MAX 0x2000
 
 #define WM_IGNORE_SIZE (WM_USER + 2)
 #define WM_IGNORE_CLIP (WM_USER + 3)
@@ -78,6 +83,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     MSG msg;
     int guess_width, guess_height;
 
+    putty_inst = inst;
+
     winsock_ver = MAKEWORD(1, 1);
     if (WSAStartup(winsock_ver, &wsadata)) {
        MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
@@ -100,12 +107,33 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     {
        char *p;
 
+       default_protocol = DEFAULT_PROTOCOL;
+       default_port = DEFAULT_PORT;
+
        do_defaults(NULL);
 
        p = cmdline;
        while (*p && isspace(*p)) p++;
 
        /*
+        * Process command line options first. Yes, this can be
+        * done better, and it will be as soon as I have the
+        * energy...
+        */
+       while (*p == '-') {
+           char *q = p + strcspn(p, " \t");
+           p++;
+           if (q == p + 3 &&
+               tolower(p[0]) == 's' &&
+               tolower(p[1]) == 's' &&
+               tolower(p[2]) == 'h') {
+               default_protocol = cfg.protocol = PROT_SSH;
+               default_port = cfg.port = 22;
+           }
+           p = q + strspn(q, " \t");
+       }
+
+       /*
         * An initial @ means to activate a saved session.
         */
        if (*p == '@') {
@@ -123,7 +151,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
             */
            HANDLE filemap;
            Config *cp;
-           if (sscanf(p+1, "%x", &filemap) == 1 &&
+           if (sscanf(p+1, "%p", &filemap) == 1 &&
                (cp = MapViewOfFile(filemap, FILE_MAP_READ,
                                    0, 0, sizeof(Config))) != NULL) {
                cfg = *cp;
@@ -153,7 +181,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        }
     }
 
-    back = (cfg.protocol == PROT_SSH ? &ssh_backend : &telnet_backend);
+    back = (cfg.protocol == PROT_SSH ? &ssh_backend : 
+           cfg.protocol == PROT_TELNET ? &telnet_backend :
+           &raw_backend);
+
+    ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
 
     if (!prev) {
        wndclass.style         = 0;
@@ -163,7 +195,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
        wndclass.hInstance     = inst;
        wndclass.hIcon         = LoadIcon (inst,
                                           MAKEINTRESOURCE(IDI_MAINICON));
-       wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
+       wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
        wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
        wndclass.lpszMenuName  = NULL;
        wndclass.lpszClassName = appname;
@@ -267,7 +299,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
            return 0;
        }
        window_name = icon_name = NULL;
-       sprintf(msg, "PuTTY: %s", realhost);
+       sprintf(msg, "%s - PuTTY", realhost);
        set_title (msg);
        set_icon (msg);
     }
@@ -278,6 +310,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
     inbuf_reap = inbuf_head = 0;
     outbuf_reap = outbuf_head = 0;
 
+    /* 
+     * Choose unscroll method
+     */
+    unscroll_event = US_DISP;
+
     /*
      * Prepare the mouse handler.
      */
@@ -290,7 +327,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
      */
     {
        HMENU m = GetSystemMenu (hwnd, FALSE);
-       HMENU p;
+       HMENU p,s;
+       int i;
 
        AppendMenu (m, MF_SEPARATOR, 0, 0);
        if (cfg.protocol == PROT_TELNET) {
@@ -312,11 +350,17 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
            AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
            AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
            AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
-           AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Show Negotiation");
            AppendMenu (m, MF_SEPARATOR, 0, 0);
        }
+       AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Event Log");
+       AppendMenu (m, MF_SEPARATOR, 0, 0);
        AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "New 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, "Saved Sessions");
        AppendMenu (m, MF_ENABLED, IDM_RECONF, "Change Settings");
        AppendMenu (m, MF_SEPARATOR, 0, 0);
        AppendMenu (m, MF_ENABLED, IDM_CLRSB, "Clear Scrollback");
@@ -472,8 +516,8 @@ static void init_fonts(void) {
                           CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
                           FIXED_PITCH | FF_DONTCARE, cfg.font)
     if (cfg.vtmode != VT_OEMONLY) {
-       f(FONT_NORMAL, ANSI_CHARSET, fw_dontcare, FALSE);
-       f(FONT_UNDERLINE, ANSI_CHARSET, fw_dontcare, TRUE);
+       f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
+       f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
     }
     if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) {
        f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
@@ -481,8 +525,8 @@ static void init_fonts(void) {
     }
     if (bold_mode == BOLD_FONT) {
        if (cfg.vtmode != VT_OEMONLY) {
-           f(FONT_BOLD, ANSI_CHARSET, fw_bold, FALSE);
-           f(FONT_BOLDUND, ANSI_CHARSET, fw_bold, TRUE);
+           f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
+           f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
        }
        if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) {
            f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
@@ -556,7 +600,9 @@ void request_resize (int w, int h) {
 }
 
 static void click (Mouse_Button b, int x, int y) {
-    if (lastbtn == b && GetMessageTime() - lasttime < dbltime) {
+    int thistime = GetMessageTime();
+
+    if (lastbtn == b && thistime - lasttime < dbltime) {
        lastact = (lastact == MA_CLICK ? MA_2CLK :
                   lastact == MA_2CLK ? MA_3CLK :
                   lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
@@ -566,7 +612,7 @@ static void click (Mouse_Button b, int x, int y) {
     }
     if (lastact != MA_NOTHING)
        term_mouse (b, lastact, x, y);
-    lasttime = GetMessageTime();
+    lasttime = thistime;
 }
 
 static int WINAPI WndProc (HWND hwnd, UINT message,
@@ -579,19 +625,28 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
     switch (message) {
       case WM_CREATE:
        break;
+      case WM_CLOSE:
+       if (!cfg.warn_on_close ||
+           MessageBox(hwnd, "Are you sure you want to close this session?",
+                      "PuTTY Exit Confirmation",
+                      MB_ICONWARNING | MB_OKCANCEL) == IDOK)
+           DestroyWindow(hwnd);
+       return 0;
       case WM_DESTROY:
        PostQuitMessage (0);
        return 0;
       case WM_SYSCOMMAND:
-       switch (wParam) {
+       switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
          case IDM_SHOWLOG:
-           shownegot(hwnd);
+           showeventlog(hwnd);
            break;
          case IDM_NEWSESS:
          case IDM_DUPSESS:
+         case IDM_SAVEDSESS:
            {
                char b[2048];
                char c[30], *cl;
+               int freecl = FALSE;
                STARTUPINFO si;
                PROCESS_INFORMATION pi;
                HANDLE filemap = NULL;
@@ -622,8 +677,17 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
                            UnmapViewOfFile(p);
                        }
                    }
-                   sprintf(c, "putty &%08x", filemap);
+                   sprintf(c, "putty &%p", filemap);
                    cl = c;
+               } else if (wParam == IDM_SAVEDSESS) {
+                   char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
+                   cl = malloc(16 + strlen(session)); /* 8, but play safe */
+                   if (!cl)
+                       cl = NULL;     /* not a very important failure mode */
+                   else {
+                       sprintf(cl, "putty @%s", session);
+                       freecl = TRUE;
+                   }
                } else
                    cl = NULL;
 
@@ -640,6 +704,8 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
 
                if (filemap)
                    CloseHandle(filemap);
+               if (freecl)
+                   free(cl);
            }
            break;
          case IDM_RECONF:
@@ -656,6 +722,7 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
            und_mode = UND_FONT;
            init_fonts();
            sfree(logpal);
+           ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
            if (pal)
                DeleteObject(pal);
            logpal = NULL;
@@ -696,44 +763,51 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
          case IDM_ABOUT:
            showabout (hwnd);
            break;
+       default:
+         if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
+           SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
+         }
        }
        break;
 
 #define X_POS(l) ((int)(short)LOWORD(l))
 #define Y_POS(l) ((int)(short)HIWORD(l))
 
+#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)
+
       case WM_LBUTTONDOWN:
-       click (MB_SELECT, X_POS(lParam) / font_width,
-              Y_POS(lParam) / font_height);
+       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, X_POS(lParam) / font_width,
-                   Y_POS(lParam) / font_height);
+       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,
-              X_POS(lParam) / font_width,
-              Y_POS(lParam) / font_height);
+              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, X_POS(lParam) / font_width,
-                   Y_POS(lParam) / font_height);
-       return 0;
+                   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,
-              X_POS(lParam) / font_width,
-              Y_POS(lParam) / font_height);
+              TO_CHR_X(X_POS(lParam)),
+              TO_CHR_Y(Y_POS(lParam)));
        return 0;
       case WM_RBUTTONUP:
        term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
-                   MA_RELEASE, X_POS(lParam) / font_width,
-                   Y_POS(lParam) / font_height);
+                   MA_RELEASE, TO_CHR_X(X_POS(lParam)),
+                   TO_CHR_Y(Y_POS(lParam)));
         ReleaseCapture();
        return 0;
       case WM_MOUSEMOVE:
@@ -752,12 +826,9 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
                b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
            else
                b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
-           term_mouse (b, MA_DRAG, X_POS(lParam) / font_width,
-                       Y_POS(lParam) / font_height);
+           term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
+                       TO_CHR_Y(Y_POS(lParam)));
        }
-       lastbtn = MB_NOTHING;
-       lastact = MA_NOTHING;
-       lasttime = GetMessageTime();
        return 0;
       case WM_IGNORE_CLIP:
        ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
@@ -822,6 +893,12 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
       case WM_IGNORE_SIZE:
        ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
        break;
+      case WM_ENTERSIZEMOVE:
+          EnableSizeTip(1);
+          break;
+      case WM_EXITSIZEMOVE:
+          EnableSizeTip(0);
+          break;
       case WM_SIZING:
        {
            int width, height, w, h, ew, eh;
@@ -831,6 +908,7 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
            height = r->bottom - r->top - extra_height;
            w = (width + font_width/2) / font_width; if (w < 1) w = 1;
            h = (height + font_height/2) / font_height; if (h < 1) h = 1;
+        UpdateSizeTip(hwnd, w, h);
            ew = width - w * font_width;
            eh = height - h * font_height;
            if (ew != 0) {
@@ -864,7 +942,10 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
        if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
            SetWindowText (hwnd, window_name);
        if (!ignore_size) {
-           int width, height, w, h, ew, eh;
+           int width, height, w, h;
+#if 0 /* we have fixed this using WM_SIZING now */
+            int ew, eh;
+#endif
 
            width = LOWORD(lParam);
            height = HIWORD(lParam);
@@ -945,9 +1026,38 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
            int len;
 
            len = TranslateKey (wParam, lParam, buf);
-           back->send (buf, len);
+           if (len == -1)
+               return DefWindowProc (hwnd, message, wParam, lParam);
+           ldisc->send (buf, len);
        }
        return 0;
+      case WM_KEYUP:
+      case WM_SYSKEYUP:
+       /*
+        * We handle KEYUP ourselves in order to distinghish left
+        * and right Alt or Control keys, which Windows won't do
+        * right if left to itself. See also the special processing
+        * at the top of TranslateKey.
+        */
+       {
+            BYTE keystate[256];
+            int ret = GetKeyboardState(keystate);
+            if (ret && wParam == VK_MENU) {
+               if (lParam & 0x1000000) keystate[VK_RMENU] = 0;
+               else keystate[VK_LMENU] = 0;
+                SetKeyboardState (keystate);
+            }
+            if (ret && wParam == VK_CONTROL) {
+               if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0;
+               else keystate[VK_LCONTROL] = 0;
+                SetKeyboardState (keystate);
+            }
+       }
+        /*
+         * We don't return here, in order to allow Windows to do
+         * its own KEYUP processing as well.
+         */
+       break;
       case WM_CHAR:
       case WM_SYSCHAR:
        /*
@@ -957,8 +1067,8 @@ static int WINAPI WndProc (HWND hwnd, UINT message,
         * we're ready to cope.
         */
        {
-           char c = wParam;
-           back->send (&c, 1);
+           char c = xlat_kbd2tty((unsigned char)wParam);
+           ldisc->send (&c, 1);
        }
        return 0;
     }
@@ -1082,18 +1192,24 @@ void do_text (Context ctx, int x, int y, char *text, int len,
        TextOut (hdc, x-1, y, text, len);
     }
     if (und_mode == UND_LINE && (attr & ATTR_UNDER)) {
-       SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
+        HPEN oldpen;
+       oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
        MoveToEx (hdc, x, y+descent, NULL);
        LineTo (hdc, x+len*font_width, y+descent);
+        oldpen = SelectObject (hdc, oldpen);
+        DeleteObject (oldpen);
     }
     if (attr & ATTR_PASCURS) {
        POINT pts[5];
+        HPEN oldpen;
        pts[0].x = pts[1].x = pts[4].x = x;
        pts[2].x = pts[3].x = x+font_width-1;
        pts[0].y = pts[3].y = pts[4].y = y;
        pts[1].y = pts[2].y = y+font_height-1;
-       SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
+       oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
        Polyline (hdc, pts, 5);
+        oldpen = SelectObject (hdc, oldpen);
+        DeleteObject (oldpen);
     }
 }
 
@@ -1105,18 +1221,71 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
     unsigned char *p = output;
     BYTE keystate[256];
     int ret, code;
+    int cancel_alt = FALSE;
+
+    /*
+     * Get hold of the keyboard state, because we'll need it a few
+     * times shortly.
+     */
+    ret = GetKeyboardState(keystate);
+
+    /* 
+     * Record that we pressed key so the scroll window can be reset, but
+     * be careful to avoid Shift-UP/Down
+     */
+    if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
+        seen_key_event = 1; 
+    }
+
+    /* 
+     * Windows does not always want to distinguish left and right
+     * Alt or Control keys. Thus we keep track of them ourselves.
+     * See also the WM_KEYUP handler.
+     */
+    if (wParam == VK_MENU) {
+        if (lParam & 0x1000000) keystate[VK_RMENU] = 0x80;
+        else keystate[VK_LMENU] = 0x80;
+        SetKeyboardState (keystate);
+        return 0;
+    }
+    if (wParam == VK_CONTROL) {
+        if (lParam & 0x1000000) keystate[VK_RCONTROL] = 0x80;
+        else keystate[VK_LCONTROL] = 0x80;
+        SetKeyboardState (keystate);
+        return 0;
+    }
 
     /*
-     * Prepend ESC if ALT was pressed at the time.
+     * Prepend ESC, and cancel ALT, if ALT was pressed at the time
+     * and it wasn't AltGr.
      */
-    if (lParam & 0x20000000)
+    if (lParam & 0x20000000 && (keystate[VK_LMENU] & 0x80)) {
         *p++ = 0x1B;
+        cancel_alt = TRUE;
+    }
 
     /*
-     * Get hold of the keyboard state, because we'll need it a few
-     * times shortly.
+     * NetHack keypad mode. This may conflict with Shift-PgUp/PgDn,
+     * so we do it first.
      */
-    ret = GetKeyboardState(keystate);
+    if (cfg.nethack_keypad) {
+       int shift = keystate[VK_SHIFT] & 0x80;
+        /*
+         * NB the shifted versions only work with numlock off.
+         */
+       switch ( (lParam >> 16) & 0x1FF ) {
+         case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output;
+         case 0x048: *p++ = shift ? 'K' : 'k'; return p - output;
+         case 0x049: *p++ = shift ? 'U' : 'u'; return p - output;
+         case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output;
+         case 0x04C: *p++ = '.'; return p - output;
+         case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output;
+         case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output;
+         case 0x050: *p++ = shift ? 'J' : 'j'; return p - output;
+         case 0x051: *p++ = shift ? 'N' : 'n'; return p - output;
+         case 0x053: *p++ = '.'; return p - output;
+       }
+    }
 
     /*
      * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
@@ -1130,9 +1299,12 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
        SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
        return 0;
     }
-    if ((lParam & 0x20000000) && wParam == VK_F4) {
-       SendMessage (hwnd, WM_DESTROY, 0, 0);
-       return 0;
+    if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) {
+       return -1;
+    }
+    if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) {
+       SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
+       return -1;
     }
 
     /*
@@ -1158,12 +1330,12 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
        return p - output;
     }
 
-    /*
-     * If we're in applications keypad mode, we have to process it
-     * before char-map translation, because it will pre-empt lots
-     * of stuff, even if NumLock is off.
-     */
     if (app_keypad_keys) {
+       /*
+        * If we're in applications keypad mode, we have to process it
+        * before char-map translation, because it will pre-empt lots
+        * of stuff, even if NumLock is off.
+        */
        if (ret) {
            /*
             * Hack to ensure NumLock doesn't interfere with
@@ -1201,23 +1373,36 @@ static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
     }
 
     /*
-     * Before doing Windows charmap translation, remove ALT from
-     * the keymap, since its sole effect should be to prepend ESC,
-     * which we've already done. Note that removal of ALT has to
-     * happen _after_ the above call to SetKeyboardState, or dire
-     * things will befall.
+     * Before doing Windows charmap translation, remove LeftALT
+     * from the keymap, since its sole effect should be to prepend
+     * ESC, which we've already done. Note that removal of LeftALT
+     * has to happen _after_ the above call to SetKeyboardState, or
+     * dire things will befall.
      */
-    keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0;
+    if (cancel_alt) {
+        keystate[VK_MENU] = keystate[VK_RMENU];
+        keystate[VK_LMENU] = 0;
+    }
 
     /*
      * Attempt the Windows char-map translation.
      */
     if (ret) {
        WORD chr;
-       int r = ToAscii (wParam, (lParam >> 16) & 0xFF,
-                        keystate, &chr, 0);
+       int r;
+       BOOL capsOn=keystate[VK_CAPITAL] !=0;
+
+       /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
+       if(cfg.xlat_capslockcyr)
+           keystate[VK_CAPITAL] = 0;
+
+       r = ToAscii (wParam, (lParam >> 16) & 0xFF,
+                    keystate, &chr, 0);
+
+       if(capsOn)
+           chr = xlat_latkbd2win((unsigned char)(chr & 0xFF));
        if (r == 1) {
-           *p++ = chr & 0xFF;
+           *p++ = xlat_kbd2tty((unsigned char)(chr & 0xFF));
            return p - output;
        }
     }
@@ -1345,10 +1530,11 @@ void set_sbar (int total, int start, int page) {
     si.nMax = total - 1;
     si.nPage = page;
     si.nPos = start;
-    SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
+    if (hwnd)
+        SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
 }
 
-Context get_ctx() {
+Context get_ctx(void) {
     HDC hdc;
     if (hwnd) {
        hdc = GetDC (hwnd);
@@ -1476,11 +1662,10 @@ void get_clip (void **p, int *len) {
  */
 void optimised_move (int to, int from, int lines) {
     RECT r;
-    int min, max, d;
+    int min, max;
 
     min = (to < from ? to : from);
     max = to + from - min;
-    d = max - min;
 
     r.left = 0; r.right = cols * font_width;
     r.top = min * font_height; r.bottom = (max+lines) * font_height;