Add a .cvsignore file
[u/mdw/putty] / window.c
index 680f065..26fcb0d 100644 (file)
--- a/window.c
+++ b/window.c
@@ -1,4 +1,5 @@
 #include <windows.h>
+#include <imm.h>
 #include <commctrl.h>
 #ifndef AUTO_WINSOCK
 #ifdef WINSOCK_TWO
@@ -39,6 +40,7 @@
 #define IDM_TEL_EOF   0x0130
 #define IDM_ABOUT     0x0140
 #define IDM_SAVEDSESS 0x0150
+#define IDM_COPYALL   0x0160
 
 #define IDM_SAVED_MIN 0x1000
 #define IDM_SAVED_MAX 0x2000
@@ -101,6 +103,8 @@ static char *window_name, *icon_name;
 
 static Ldisc *real_ldisc;
 
+static int compose_state = 0;
+
 void begin_session(void) {
     ldisc = real_ldisc;
 }
@@ -145,6 +149,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);
 
@@ -165,11 +170,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' &&
@@ -310,6 +310,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
         }
     }
 
+    /* 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;
+    }
+
     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... */
@@ -364,14 +372,15 @@ 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 exwinmode = 0;
+       if (!cfg.scrollbar)  winmode &= ~(WS_VSCROLL);
+       if (cfg.locksize)    winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
+        if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
+        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
@@ -408,10 +417,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.
@@ -500,15 +511,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);
@@ -521,6 +533,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;
@@ -588,7 +605,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
            if (!has_focus)
               timer_id = SetTimer(hwnd, 1, 59500, NULL);
            else
-              timer_id = SetTimer(hwnd, 1, 250, NULL);
+              timer_id = SetTimer(hwnd, 1, 100, NULL);
            long_timer = 1;
        
            /* There's no point rescanning everything in the message queue
@@ -1044,6 +1061,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:
@@ -1124,7 +1142,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 {
@@ -1148,82 +1166,140 @@ 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;
+               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);
+                /*
+                 * 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);
+                    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 = 0;
+                            SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+                                         SWP_NOMOVE | SWP_NOSIZE);
+                        }
+                    }
+
+                    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)
+                   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;
@@ -1345,8 +1421,9 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
        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;
@@ -1362,11 +1439,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:
        {
@@ -1442,6 +1521,8 @@ static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
                  */
                 if (!resizing)
                     back->size();
+               else
+                   need_backend_resize = TRUE;
                just_reconfigged = FALSE;
            }
        }
@@ -1582,7 +1663,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;
     }
@@ -1743,7 +1824,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;
@@ -1755,6 +1836,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) {
@@ -1810,12 +1919,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_state = 0;
     static int compose_char = 0;
     static WPARAM compose_key = 0;
-
+    
     r = GetKeyboardState(keystate);
     if (!r) memset(keystate, 0, sizeof(keystate));
     else
@@ -1848,7 +1959,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));
 
@@ -1877,11 +1988,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        }
 #endif
 
+       if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
+           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))
            {
-               keystate[VK_RMENU] = keystate[VK_MENU];
                if (!compose_state) compose_key = wParam;
            }
            if (wParam == VK_APPS && !compose_state)
@@ -1898,11 +2012,13 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            }
            else if (compose_state==1 && wParam != VK_CONTROL)
                compose_state = 0;
-       } else
+       } else {
            compose_state = 0;
+       }
 
        /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
-       if ( (cfg.funky_type == 3 || (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;
@@ -1947,7 +2063,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
     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)
+    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)
@@ -1999,7 +2116,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
             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;
        }
@@ -2029,13 +2147,14 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
           int xkey = 0;
 
           if ( cfg.funky_type == 3 ||
-             ( cfg.funky_type <= 1 && app_keypad_keys)) switch(wParam) {
+             ( 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;
@@ -2197,7 +2316,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            {
                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);
@@ -2225,7 +2344,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;
@@ -2271,9 +2390,16 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        }
     }
 
-    /* This stops ALT press-release doing a 'COMMAND MENU' function */
-    if (!cfg.alt_only) {
-       if (message == WM_SYSKEYUP && wParam == VK_MENU) 
+    /* 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;
     }