]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve handling of Unicode keyboard input on MS-Windows
authorIlya Zakharevich <ilya@math.berkeley.edu>
Fri, 31 Jul 2015 08:50:23 +0000 (11:50 +0300)
committerEli Zaretskii <eliz@gnu.org>
Fri, 31 Jul 2015 08:50:23 +0000 (11:50 +0300)
* src/w32fns.c (get_wm_chars, deliver_wm_chars): New functions.
(FPRINTF_WM_CHARS) [DEBUG_WM_CHARS]: New macro for debugging.
(w32_wnd_proc): Call deliver_wm_chars to process non-special keys
upon receiving WM_KEYDOWN or WM_SYSKEYDOWN messages.  If that is
successful, don't call TranslateMessage.  (Bug#19994)

src/w32fns.c

index c742ca8534ffd323e5b20ac37ed8a57850012015..fecdf7cb72d52361e719e3154bd66c8059ca777d 100644 (file)
@@ -270,7 +270,7 @@ check_x_display_info (Lisp_Object object)
       struct terminal *t = decode_live_terminal (object);
 
       if (t->type != output_w32)
-        error ("Terminal %d is not a W32 display", t->id);
+       error ("Terminal %d is not a W32 display", t->id);
 
       return t->display_info.w32;
     }
@@ -304,7 +304,7 @@ x_window_to_frame (struct w32_display_info *dpyinfo, HWND wdesc)
        continue;
 
       if (FRAME_W32_WINDOW (f) == wdesc)
-        return f;
+       return f;
     }
   return 0;
 }
@@ -366,7 +366,7 @@ w32_fullscreen_rect (HWND hwnd, int fsmode, RECT normal, RECT *rect)
   if (monitor_from_window_fn && get_monitor_info_fn)
     {
       HMONITOR monitor =
-        monitor_from_window_fn (hwnd, MONITOR_DEFAULT_TO_NEAREST);
+       monitor_from_window_fn (hwnd, MONITOR_DEFAULT_TO_NEAREST);
       get_monitor_info_fn (monitor, &mi);
     }
   else
@@ -949,8 +949,8 @@ x_to_w32_color (const char * colorname)
            break;
          val = (UINT)(0x100 * value);
          /* We used 0x100 instead of 0xFF to give a continuous
-             range between 0.0 and 1.0 inclusive.  The next statement
-             fixes the 1.0 case.  */
+            range between 0.0 and 1.0 inclusive.  The next statement
+            fixes the 1.0 case.  */
          if (val == 0x100)
            val = 0xFF;
          colorval |= (val << pos);
@@ -1060,7 +1060,7 @@ w32_map_color (struct frame *f, COLORREF color)
   while (list)
     {
       if (W32_COLOR (list->entry) == color)
-        {
+       {
          ++list->refcount;
          return;
        }
@@ -1092,7 +1092,7 @@ w32_unmap_color (struct frame *f, COLORREF color)
   while (list)
     {
       if (W32_COLOR (list->entry) == color)
-        {
+       {
          if (--list->refcount == 0)
            {
              *prev = list->next;
@@ -1121,9 +1121,9 @@ gamma_correct (struct frame *f, COLORREF *color)
   if (f->gamma)
     {
       *color = PALETTERGB (
-        pow (GetRValue (*color) / 255.0, f->gamma) * 255.0 + 0.5,
-        pow (GetGValue (*color) / 255.0, f->gamma) * 255.0 + 0.5,
-        pow (GetBValue (*color) / 255.0, f->gamma) * 255.0 + 0.5);
+       pow (GetRValue (*color) / 255.0, f->gamma) * 255.0 + 0.5,
+       pow (GetGValue (*color) / 255.0, f->gamma) * 255.0 + 0.5,
+       pow (GetBValue (*color) / 255.0, f->gamma) * 255.0 + 0.5);
     }
 }
 
@@ -1144,12 +1144,12 @@ w32_defined_color (struct frame *f, const char *color, XColor *color_def,
   if (!NILP (tem))
     {
       if (f)
-        {
-          /* Apply gamma correction.  */
-          w32_color_ref = XUINT (tem);
-          gamma_correct (f, &w32_color_ref);
-          XSETINT (tem, w32_color_ref);
-        }
+       {
+         /* Apply gamma correction.  */
+         w32_color_ref = XUINT (tem);
+         gamma_correct (f, &w32_color_ref);
+         XSETINT (tem, w32_color_ref);
+       }
 
       /* Map this color to the palette if it is enabled. */
       if (!NILP (Vw32_enable_palette))
@@ -1258,7 +1258,7 @@ x_set_foreground_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 
       update_face_from_frame_parameter (f, Qforeground_color, arg);
       if (FRAME_VISIBLE_P (f))
-        redraw_frame (f);
+       redraw_frame (f);
     }
 }
 
@@ -1271,12 +1271,12 @@ x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
   if (FRAME_W32_WINDOW (f) != 0)
     {
       SetWindowLong (FRAME_W32_WINDOW (f), WND_BACKGROUND_INDEX,
-                     FRAME_BACKGROUND_PIXEL (f));
+                    FRAME_BACKGROUND_PIXEL (f));
 
       update_face_from_frame_parameter (f, Qbackground_color, arg);
 
       if (FRAME_VISIBLE_P (f))
-        redraw_frame (f);
+       redraw_frame (f);
     }
 }
 
@@ -1399,9 +1399,9 @@ x_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
     XRecolorCursor (FRAME_W32_DISPLAY (f), mode_cursor,
                    &fore_color, &back_color);
     XRecolorCursor (FRAME_W32_DISPLAY (f), hand_cursor,
-                    &fore_color, &back_color);
+                   &fore_color, &back_color);
     XRecolorCursor (FRAME_W32_DISPLAY (f), hourglass_cursor,
-                    &fore_color, &back_color);
+                   &fore_color, &back_color);
   }
 
   if (FRAME_W32_WINDOW (f) != 0)
@@ -1445,7 +1445,7 @@ x_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
 
   if (!NILP (Vx_cursor_fore_pixel))
     fore_pixel = x_decode_color (f, Vx_cursor_fore_pixel,
-                                 WHITE_PIX_DEFAULT (f));
+                                WHITE_PIX_DEFAULT (f));
   else
     fore_pixel = FRAME_BACKGROUND_PIXEL (f);
 
@@ -1494,7 +1494,7 @@ x_set_border_pixel (struct frame *f, int pix)
   if (FRAME_W32_WINDOW (f) != 0 && f->border_width > 0)
     {
       if (FRAME_VISIBLE_P (f))
-        redraw_frame (f);
+       redraw_frame (f);
     }
 }
 
@@ -1771,7 +1771,7 @@ w32_set_title_bar_text (struct frame *f, Lisp_Object name)
       block_input ();
 #ifdef __CYGWIN__
       GUI_FN (SetWindowText) (FRAME_W32_WINDOW (f),
-                              GUI_SDATA (GUI_ENCODE_SYSTEM (name)));
+                             GUI_SDATA (GUI_ENCODE_SYSTEM (name)));
 #else
       /* The frame's title many times shows the name of the file
         visited in the selected window's buffer, so it makes sense to
@@ -2319,7 +2319,7 @@ w32_get_modifiers (void)
          (modifier_set (VK_RWIN)    ? w32_key_to_modifier (VK_RWIN) : 0) |
          (modifier_set (VK_APPS)    ? w32_key_to_modifier (VK_APPS) : 0) |
          (modifier_set (VK_SCROLL)  ? w32_key_to_modifier (VK_SCROLL) : 0) |
-          (modifier_set (VK_MENU)    ?
+         (modifier_set (VK_MENU)    ?
           ((NILP (Vw32_alt_is_meta)) ? alt_modifier : meta_modifier) : 0));
 }
 
@@ -2541,13 +2541,13 @@ w32_msg_pump (deferred_msg * msg_buf)
              /* Produced by complete_deferred_msg; just ignore.  */
              break;
            case WM_EMACS_CREATEWINDOW:
-              /* Initialize COM for this window. Even though we don't use it,
-                 some third party shell extensions can cause it to be used in
-                 system dialogs, which causes a crash if it is not initialized.
-                 This is a known bug in Windows, which was fixed long ago, but
-                 the patch for XP is not publicly available until XP SP3,
-                 and older versions will never be patched.  */
-              CoInitialize (NULL);
+             /* Initialize COM for this window. Even though we don't use it,
+                some third party shell extensions can cause it to be used in
+                system dialogs, which causes a crash if it is not initialized.
+                This is a known bug in Windows, which was fixed long ago, but
+                the patch for XP is not publicly available until XP SP3,
+                and older versions will never be patched.  */
+             CoInitialize (NULL);
              w32_createwindow ((struct frame *) msg.wParam,
                                (int *) msg.lParam);
              if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
@@ -2577,9 +2577,9 @@ w32_msg_pump (deferred_msg * msg_buf)
              if (focus_window != NULL)
                UnregisterHotKey (focus_window, RAW_HOTKEY_ID (msg.wParam));
              /* Mark item as erased.  NB: this code must be
-                 thread-safe.  The next line is okay because the cons
-                 cell is never made into garbage and is not relocated by
-                 GC.  */
+                thread-safe.  The next line is okay because the cons
+                cell is never made into garbage and is not relocated by
+                GC.  */
              XSETCAR (make_lisp_ptr ((void *)msg.lParam, Lisp_Cons), Qnil);
              if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
                emacs_abort ();
@@ -2612,10 +2612,10 @@ w32_msg_pump (deferred_msg * msg_buf)
              }
              break;
 #ifdef MSG_DEBUG
-              /* Broadcast messages make it here, so you need to be looking
-                 for something in particular for this to be useful.  */
+             /* Broadcast messages make it here, so you need to be looking
+                for something in particular for this to be useful.  */
            default:
-              DebPrint (("msg %x not expected by w32_msg_pump\n", msg.message));
+             DebPrint (("msg %x not expected by w32_msg_pump\n", msg.message));
 #endif
            }
        }
@@ -2809,19 +2809,19 @@ post_character_message (HWND hwnd, UINT msg,
        signal_quit ();
 
        /* As a safety precaution, forcibly complete any deferred
-           messages.  This is a kludge, but I don't see any particularly
-           clean way to handle the situation where a deferred message is
-           "dropped" in the lisp thread, and will thus never be
-           completed, eg. by the user trying to activate the menubar
-           when the lisp thread is busy, and then typing C-g when the
-           menubar doesn't open promptly (with the result that the
-           menubar never responds at all because the deferred
-           WM_INITMENU message is never completed).  Another problem
-           situation is when the lisp thread calls SendMessage (to send
-           a window manager command) when a message has been deferred;
-           the lisp thread gets blocked indefinitely waiting for the
-           deferred message to be completed, which itself is waiting for
-           the lisp thread to respond.
+          messages.  This is a kludge, but I don't see any particularly
+          clean way to handle the situation where a deferred message is
+          "dropped" in the lisp thread, and will thus never be
+          completed, eg. by the user trying to activate the menubar
+          when the lisp thread is busy, and then typing C-g when the
+          menubar doesn't open promptly (with the result that the
+          menubar never responds at all because the deferred
+          WM_INITMENU message is never completed).  Another problem
+          situation is when the lisp thread calls SendMessage (to send
+          a window manager command) when a message has been deferred;
+          the lisp thread gets blocked indefinitely waiting for the
+          deferred message to be completed, which itself is waiting for
+          the lisp thread to respond.
 
           Note that we don't want to block the input thread waiting for
           a response from the lisp thread (although that would at least
@@ -2836,6 +2836,407 @@ post_character_message (HWND hwnd, UINT msg,
   my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
 }
 
+static int
+get_wm_chars (HWND aWnd, int *buf, int buflen, int ignore_ctrl, int ctrl,
+             int *ctrl_cnt, int *is_dead, int vk, int exp)
+{
+  MSG msg;
+  /* If doubled is at the end, ignore it.  */
+  int i = buflen, doubled = 0, code_unit;
+
+  if (ctrl_cnt)
+    *ctrl_cnt = 0;
+  if (is_dead)
+    *is_dead = -1;
+  eassert(w32_unicode_gui);
+  while (buflen
+        /* Should be called only when w32_unicode_gui:  */
+        && PeekMessageW(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST,
+                     PM_NOREMOVE | PM_NOYIELD)
+        && (msg.message == WM_CHAR || msg.message == WM_SYSCHAR
+            || msg.message == WM_DEADCHAR || msg.message == WM_SYSDEADCHAR
+            || msg.message == WM_UNICHAR))
+    {
+      /* We extract character payload, but in this call we handle only the
+        characters which comes BEFORE the next keyup/keydown message.  */
+      int dead;
+
+      GetMessageW(&msg, aWnd, msg.message, msg.message);
+      dead = (msg.message == WM_DEADCHAR || msg.message == WM_SYSDEADCHAR);
+      if (is_dead)
+       *is_dead = (dead ? msg.wParam : -1);
+      if (dead)
+       continue;
+      code_unit = msg.wParam;
+      if (doubled)
+       {
+         /* Had surrogate.  */
+         if (msg.message == WM_UNICHAR
+             || code_unit < 0xDC00 || code_unit > 0xDFFF)
+           { /* Mismatched first surrogate.
+                Pass both code units as if they were two characters.  */
+             *buf++ = doubled;
+             if (!--buflen)
+               return i; /* Drop the 2nd char if at the end of the buffer.  */
+           }
+         else /* see https://en.wikipedia.org/wiki/UTF-16 */
+           code_unit = (doubled << 10) + code_unit - 0x35FDC00;
+         doubled = 0;
+       }
+      else if (code_unit >= 0xD800 && code_unit <= 0xDBFF)
+       {
+         /* Handle mismatched 2nd surrogate the same as a normal character.  */
+         doubled = code_unit;
+         continue;
+       }
+
+      /* The only "fake" characters delivered by ToUnicode() or
+        TranslateMessage() are:
+        0x01 .. 0x1a for Ctrl-letter, Enter, Tab, Ctrl-Break, Esc, Backspace
+        0x00 and 0x1b .. 0x1f for Control- []\@^_
+        0x7f for Control-BackSpace
+        0x20 for Control-Space */
+      if (ignore_ctrl
+         && (code_unit < 0x20 || code_unit == 0x7f
+             || (code_unit == 0x20 && ctrl)))
+       {
+         /* Non-character payload in a WM_CHAR
+            (Ctrl-something pressed, see above).  Ignore, and report.  */
+         if (ctrl_cnt)
+           *ctrl_cnt++;
+         continue;
+       }
+      /* Traditionally, Emacs would ignore the character payload of VK_NUMPAD*
+        keys, and would treat them later via `function-key-map'.  In addition
+        to usual 102-key NUMPAD keys, this map also treats `kp-'-variants of
+        space, tab, enter, separator, equal.  TAB  and EQUAL, apparently,
+        cannot be generated on Win-GUI branch.  ENTER is already handled
+        by the code above.  According to `lispy_function_keys', kp_space is
+        generated by not-extended VK_CLEAR.  (kp-tab !=  VK_OEM_NEC_EQUAL!).
+
+        We do similarly for backward-compatibility, but ignore only the
+        characters restorable later by `function-key-map'.  */
+      if (code_unit < 0x7f
+         && ((vk >= VK_NUMPAD0 && vk <= VK_DIVIDE)
+             || (exp && ((vk >= VK_PRIOR && vk <= VK_DOWN) ||
+                    vk == VK_INSERT || vk == VK_DELETE || vk == VK_CLEAR)))
+         && strchr("0123456789/*-+.,", code_unit))
+       continue;
+      *buf++ = code_unit;
+      buflen--;
+    }
+  return i - buflen;
+}
+
+#ifdef DBG_WM_CHARS
+#  define FPRINTF_WM_CHARS(ARG)        fprintf ARG
+#else
+#  define FPRINTF_WM_CHARS(ARG)        0
+#endif
+
+/* This is a heuristic only.  This is supposed to track the state of the
+   finite automaton in the language environment of Windows.
+
+   However, separate windows (if with the same different language
+   environments!) should  have different values.  Moreover, switching to a
+   non-Emacs window with the same language environment, and using (dead)keys
+   there would change the value stored in the kernel, but not this value.  */
+static int after_deadkey = 0;
+
+int
+deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, UINT wParam,
+                 UINT lParam, int legacy_alt_meta)
+{
+  /* An "old style" keyboard description may assign up to 125 UTF-16 code
+     points to a keypress.
+     (However, the "old style" TranslateMessage() would deliver at most 16 of
+     them.)  Be on a safe side, and prepare to treat many more.  */
+  int ctrl_cnt, buf[1024], count, is_dead, after_dead = (after_deadkey != -1);
+
+  /* Since the keypress processing logic of Windows has a lot of state, it
+     is important to call TranslateMessage() for every keyup/keydown, AND
+     do it exactly once.  (The actual change of state is done by
+     ToUnicode[Ex](), which is called by TranslateMessage().  So one can
+     call ToUnicode[Ex]() instead.)
+
+     The "usual" message pump calls TranslateMessage() for EVERY event.
+     Emacs calls TranslateMessage() very selectively (is it needed for doing
+     some tricky stuff with Win95???  With newer Windows, selectiveness is,
+     most probably, not needed - and harms a lot).
+
+     So, with the usual message pump, the following call to TranslateMessage()
+     is not needed (and is going to be VERY harmful).  With Emacs' message
+     pump, the call is needed.  */
+  if (do_translate)
+    {
+      MSG windows_msg = { hwnd, msg, wParam, lParam, 0, {0,0} };
+
+      windows_msg.time = GetMessageTime ();
+      TranslateMessage (&windows_msg);
+    }
+  count = get_wm_chars (hwnd, buf, sizeof(buf)/sizeof(*buf), 1,
+                       /* The message may have been synthesized by
+                          who knows what; be conservative.  */
+                       modifier_set (VK_LCONTROL)
+                         || modifier_set (VK_RCONTROL)
+                         || modifier_set (VK_CONTROL),
+                       &ctrl_cnt, &is_dead, wParam,
+                       (lParam & 0x1000000L) != 0);
+  if (count)
+    {
+      W32Msg wmsg;
+      DWORD console_modifiers = construct_console_modifiers ();
+      int *b = buf, strip_Alt = 1, strip_ExtraMods = 1, hairy = 0;
+      char *type_CtrlAlt = NULL;
+
+      /*  XXXX In fact, there may be another case when we need to do the same:
+              What happens if the string defined in the LIGATURES has length
+              0?  Probably, we will get count==0, but the state of the finite
+              automaton would reset to 0???  */
+     after_deadkey = -1;
+
+      /* wParam is checked when converting CapsLock to Shift; this is a clone
+        of w32_get_key_modifiers ().  */
+      wmsg.dwModifiers = w32_kbd_mods_to_emacs (console_modifiers, wParam);
+
+      /* What follows is just heuristics; the correct treatement requires
+        non-destructive ToUnicode():
+          http://search.cpan.org/~ilyaz/UI-KeyboardLayout/lib/UI/KeyboardLayout.pm#Can_an_application_on_Windows_accept_keyboard_events?_Part_IV:_application-specific_modifiers
+
+        What one needs to find is:
+          * which of the present modifiers AFFECT the resulting char(s)
+            (so should be stripped, since their EFFECT is "already
+             taken into account" in the string in buf), and
+          * which modifiers are not affecting buf, so should be reported to
+            the application for further treatment.
+
+        Example: assume that we know:
+          (A) lCtrl+rCtrl+rAlt modifiers with VK_A key produce a Latin "f"
+              ("may be logical" in JCUKEN-flavored Russian keyboard flavors);
+          (B) removing any of lCtrl, rCtrl, rAlt changes the produced char;
+          (C) Win-modifier is not affecting the produced character
+              (this is the common case: happens with all "standard" layouts).
+
+        Suppose the user presses Win+lCtrl+rCtrl+rAlt modifiers with VK_A.
+        What is the intent of the user?  We need to guess the intent to decide
+        which event to deliver to the application.
+
+        This looks like a reasonable logic: since Win- modifier doesn't affect
+        the output string, the user was pressing Win for SOME OTHER purpose.
+        So the user wanted to generate Win-SOMETHING event.  Now, what is
+        something?  If one takes the mantra that "character payload is more
+        important than the combination of keypresses which resulted in this
+        payload", then one should ignore lCtrl+rCtrl+rAlt, ignore VK_A, and
+        assume that the user wanted to generate Win-f.
+
+        Unfortunately, without non-destructive ToUnicode(), checking (B),(C)
+        is out of question.  So we use heuristics (hopefully, covering
+        99.9999% of cases).  */
+
+      /* Another thing to watch for is a possibility to use AltGr-* and
+        Ctrl-Alt-* with different semantic.
+
+        Background: the layout defining the KLLF_ALTGR bit are treated
+        specially by the kernel: when VK_RMENU (=rightAlt, =AltGr) is pressed
+        (released), a press (release) of VK_LCONTROL is emulated (unless Ctrl
+        is already down).  As a result, any press/release of AltGr is seen
+        by applications as a press/release of lCtrl AND rAlt.  This is
+        applicable, in particular, to ToUnicode[Ex]().  (Keyrepeat is covered
+        the same way!)
+
+          NOTE: it IS possible to see bare rAlt even with KLLF_ALTGR; but this
+          requires a good finger coordination: doing (physically)
+            Down-lCtrl Down-rAlt Up-lCtrl Down-a
+          (doing quick enough, so that key repeat of rAlt [which would
+          generate new "fake" Down-lCtrl events] does not happens before 'a'
+          is down) results in no "fake" events, so the application will see
+          only rAlt down when 'a' is pressed.  (However, fake Up-lCtrl WILL
+          be generated when rAlt goes UP.)
+
+          In fact, note also that KLLF_ALTGR does not prohibit construction of
+          rCtrl-rAlt (just press them in this order!).
+
+        Moreover: "traditional" layouts do not define distinct modifier-masks
+        for VK_LMENU and VK_RMENU (same for VK_L/RCONTROL).  Instead, they
+        rely on the KLLF_ALTGR bit to make the behaviour of VK_LMENU and
+        VK_RMENU distinct.  As a corollary, for such layouts, the produced
+        character is the same for AltGr-* (=rAlt-*) and Ctrl-Alt-* (in any
+        combination of handedness).  For description of masks, see
+
+          http://search.cpan.org/~ilyaz/UI-KeyboardLayout/lib/UI/KeyboardLayout.pm#Keyboard_input_on_Windows,_Part_I:_what_is_the_kernel_doing?
+
+        By default, Emacs was using these coincidences via the following
+        heuristics: it was treating:
+         (*) keypresses with lCtrl-rAlt modifiers as if they are carrying
+             ONLY the character payload (no matter what the actual keyboard
+             was defining: if lCtrl-lAlt-b was delivering U+05df=beta, then
+             Emacs saw [beta]; if lCtrl-lAlt-b was undefined in the layout,
+             the keypress was completely ignored), and
+         (*) keypresses with the other combinations of handedness of Ctrl-Alt
+             modifiers (e.g., lCtrl-lAlt) as if they NEVER carry a character
+             payload (so they were reported "raw": if lCtrl-lAlt-b was
+             delivering beta, then Emacs saw event [C-A-b], and not [beta]).
+        This worked good for "traditional" layouts: users could type both
+        AltGr-x and Ctrl-Alt-x, and one was a character, another a bindable
+        event.
+
+        However, for layouts which deliver different characters for AltGr-x
+        and lCtrl-lAlt-x, this scheme makes the latter character unaccessible
+        in Emacs.  While it is easy to access functionality of [C-M-x] in
+        Emacs by other means (for example, by the `controlify' prefix, or
+        using lCtrl-rCtrl-x, or rCtrl-rAlt-x [in this order]), missing
+        characters cannot be reconstructed without a tedious manual work.  */
+
+      /* These two cases are often going to be distinguishable, since at most
+        one of these character is defined with KBDCTRL | KBDMENU modifier
+        bitmap.  (This heuristic breaks if both lCtrl-lAlt- AND lCtrl-rAlt-
+        are translated to modifier bitmaps distinct from KBDCTRL | KBDMENU,
+        or in the cases when lCtrl-lAlt-* and lCtrl-rAlt-* are generally
+        different, but lCtrl-lAlt-x and lCtrl-rAlt-x happen to deliver the
+        same character.)
+
+        So we have 2 chunks of info:
+          (A) is it lCtrl-rAlt-, or lCtrl-lAlt, or some other combination?
+          (B) is the delivered character defined with KBDCTRL | KBDMENU bits?
+        Basing on (A) and (B), we should decide whether to ignore the
+        delivered character.  (Before, Emacs was completely ignoring (B), and
+        was treating the 3-state of (A) as a bit.)  This means that we have 6
+        bits of customization.
+
+        Additionally, a presence of two Ctrl down may be AltGr-rCtrl-.  */
+
+      /* Strip all non-Shift modifiers if:
+       - more than one UTF-16 code point delivered (can't call VkKeyScanW ())
+       - or the character is a result of combining with a prefix key.  */
+      if (!after_dead && count == 1 && *b < 0x10000)
+       {
+         if (console_modifiers & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
+             && console_modifiers & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
+           {
+             type_CtrlAlt = "bB";   /* generic bindable Ctrl-Alt- modifiers */
+             if (console_modifiers & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
+                 == (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
+                /* double-Ctrl:
+                   e.g. AltGr-rCtrl on some layouts (in this order!) */
+               type_CtrlAlt = "dD";
+             else if (console_modifiers
+                      & (LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED)
+                      == (LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED))
+               type_CtrlAlt = "lL"; /* Ctrl-Alt- modifiers on the left */
+             else if (!NILP (Vw32_recognize_altgr)
+                      && (console_modifiers
+                          & (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED))
+                         == (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED))
+               type_CtrlAlt = "gG"; /* modifiers as in AltGr */
+           }
+         else if (wmsg.dwModifiers & (alt_modifier | meta_modifier)
+                  || (console_modifiers
+                      & (RIGHT_WIN_PRESSED | RIGHT_WIN_PRESSED
+                         | APPS_PRESSED | SCROLLLOCK_ON)))
+           {
+             /* Pure Alt (or combination of Alt, Win, APPS, scrolllock.  */
+             type_CtrlAlt = "aA";
+           }
+         if (type_CtrlAlt)
+           {
+             /* Out of bound bitmap:  */
+             SHORT r = VkKeyScanW( *b ), bitmap = 0x1FF;
+
+             FPRINTF_WM_CHARS((stderr, "VkKeyScanW %#06x %#04x\n", (int)r,
+                              wParam));
+             if ((r & 0xFF) == wParam)
+               bitmap = r>>8; /* *b is reachable via simple interface */
+             if (*type_CtrlAlt == 'a') /* Simple Alt seen */
+               {
+                 if ((bitmap & ~1) == 0) /* 1: KBDSHIFT */
+                   {
+                     /* In "traditional" layouts, Alt without Ctrl does not
+                        change the delivered character.  This detects this
+                        situation; it is safe to report this as Alt-something
+                         - as opposed to delivering the reported character
+                         without modifiers.  */
+                     if (legacy_alt_meta
+                         && *b > 0x7f && ('A' <= wParam && wParam <= 'Z'))
+                       /* For backward-compatibility with older Emacsen, let
+                          this be processed by another branch below (which
+                          would convert it to Alt-Latin char via wParam).  */
+                       return 0;
+                   }
+                 else
+                   hairy = 1;
+               }
+             /* Check whether the delivered character(s) is accessible via
+                KBDCTRL | KBDALT ( | KBDSHIFT ) modifier mask (which is 7).  */
+             else if ((bitmap & ~1) != 6)
+               {
+                 /* The character is not accessible via plain Ctrl-Alt(-Shift)
+                    (which is, probably, same as AltGr) modifiers.
+                    Either it was after a prefix key, or is combined with
+                    modifier keys which we don't see, or there is an asymmetry
+                    between left-hand and right-hand modifiers, or other hairy
+                    stuff.  */
+                 hairy = 1;
+               }
+             /* The best solution is to delegate these tough (but rarely
+                needed) choices to the user.  Temporarily (???), it is
+                implemented as C macros.
+
+                Essentially, there are 3 things to do: return 0 (handle to the
+                legacy processing code [ignoring the character payload]; keep
+                some modifiers (so that they will be processed by the binding
+                system [on top of the character payload]; strip modifiers [so
+                that `self-insert' is going to be triggered with the character
+                payload]).
+
+                The default below should cover 99.9999% of cases:
+                  (a) strip Alt- in the hairy case only;
+                      (stripping = not ignoring)
+                  (l) for lAlt-lCtrl, ignore the char in simple cases only;
+                  (g) for what looks like AltGr, ignore the modifiers;
+                  (d) for what looks like lCtrl-rCtrl-Alt (probably
+                      AltGr-rCtrl), ignore the character in simple cases only;
+                  (b) for other cases of Ctrl-Alt, ignore the character in
+                      simple cases only.
+
+                Essentially, in all hairy cases, and in looks-like-AltGr case,
+                we keep the character, ignoring the modifiers.  In all the
+                other cases, we ignore the delivered character.  */
+#define S_TYPES_TO_IGNORE_CHARACTER_PAYLOAD "aldb"
+#define S_TYPES_TO_REPORT_CHARACTER_PAYLOAD_WITH_MODIFIERS ""
+             if (strchr(S_TYPES_TO_IGNORE_CHARACTER_PAYLOAD,
+                        type_CtrlAlt[hairy]))
+               return 0;
+             /* If in neither list, report all the modifiers we see COMBINED
+                WITH the reported character.  */
+             if (strchr(S_TYPES_TO_REPORT_CHARACTER_PAYLOAD_WITH_MODIFIERS,
+                         type_CtrlAlt[hairy]))
+               strip_ExtraMods = 0;
+           }
+       }
+      if (strip_ExtraMods)
+       wmsg.dwModifiers = wmsg.dwModifiers & shift_modifier;
+
+      signal_user_input ();
+      while (count--)
+       {
+         FPRINTF_WM_CHARS((stderr, "unichar %#06x\n", *b));
+         my_post_msg (&wmsg, hwnd, WM_UNICHAR, *b++, lParam);
+       }
+      if (!ctrl_cnt) /* Process ALSO as ctrl */
+       return 1;
+      else
+       FPRINTF_WM_CHARS((stderr, "extra ctrl char\n"));
+      return -1;
+    }
+  else if (is_dead >= 0)
+    {
+      FPRINTF_WM_CHARS((stderr, "dead %#06x\n", is_dead));
+      after_deadkey = is_dead;
+      return 1;
+    }
+  return 0;
+}
+
 /* Main window procedure */
 
 static LRESULT CALLBACK
@@ -2869,15 +3270,15 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       f = x_window_to_frame (dpyinfo, hwnd);
       if (f)
        {
-          HDC hdc = get_frame_dc (f);
+         HDC hdc = get_frame_dc (f);
          GetUpdateRect (hwnd, &wmsg.rect, FALSE);
          w32_clear_rect (f, hdc, &wmsg.rect);
-          release_frame_dc (f, hdc);
+         release_frame_dc (f, hdc);
 
 #if defined (W32_DEBUG_DISPLAY)
-          DebPrint (("WM_ERASEBKGND (frame %p): erasing %d,%d-%d,%d\n",
+         DebPrint (("WM_ERASEBKGND (frame %p): erasing %d,%d-%d,%d\n",
                     f,
-                     wmsg.rect.left, wmsg.rect.top,
+                    wmsg.rect.left, wmsg.rect.top,
                     wmsg.rect.right, wmsg.rect.bottom));
 #endif /* W32_DEBUG_DISPLAY */
        }
@@ -2885,7 +3286,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_PALETTECHANGED:
       /* ignore our own changes */
       if ((HWND)wParam != hwnd)
-        {
+       {
          f = x_window_to_frame (dpyinfo, hwnd);
          if (f)
            /* get_frame_dc will realize our palette and force all
@@ -2895,24 +3296,24 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       return 0;
     case WM_PAINT:
       {
-       PAINTSTRUCT paintStruct;
-        RECT update_rect;
+       PAINTSTRUCT paintStruct;
+       RECT update_rect;
        memset (&update_rect, 0, sizeof (update_rect));
 
        f = x_window_to_frame (dpyinfo, hwnd);
        if (f == 0)
          {
-            DebPrint (("WM_PAINT received for unknown window %p\n", hwnd));
+           DebPrint (("WM_PAINT received for unknown window %p\n", hwnd));
            return 0;
          }
 
-        /* MSDN Docs say not to call BeginPaint if GetUpdateRect
-           fails.  Apparently this can happen under some
-           circumstances.  */
-        if (GetUpdateRect (hwnd, &update_rect, FALSE) || !w32_strict_painting)
-          {
-            enter_crit ();
-            BeginPaint (hwnd, &paintStruct);
+       /* MSDN Docs say not to call BeginPaint if GetUpdateRect
+          fails.  Apparently this can happen under some
+          circumstances.  */
+       if (GetUpdateRect (hwnd, &update_rect, FALSE) || !w32_strict_painting)
+         {
+           enter_crit ();
+           BeginPaint (hwnd, &paintStruct);
 
            /* The rectangles returned by GetUpdateRect and BeginPaint
               do not always match.  Play it safe by assuming both areas
@@ -2920,40 +3321,49 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
            UnionRect (&(wmsg.rect), &update_rect, &(paintStruct.rcPaint));
 
 #if defined (W32_DEBUG_DISPLAY)
-            DebPrint (("WM_PAINT (frame %p): painting %d,%d-%d,%d\n",
+           DebPrint (("WM_PAINT (frame %p): painting %d,%d-%d,%d\n",
                       f,
                       wmsg.rect.left, wmsg.rect.top,
                       wmsg.rect.right, wmsg.rect.bottom));
-            DebPrint (("  [update region is %d,%d-%d,%d]\n",
-                       update_rect.left, update_rect.top,
-                       update_rect.right, update_rect.bottom));
+           DebPrint (("  [update region is %d,%d-%d,%d]\n",
+                      update_rect.left, update_rect.top,
+                      update_rect.right, update_rect.bottom));
 #endif
-            EndPaint (hwnd, &paintStruct);
-            leave_crit ();
+           EndPaint (hwnd, &paintStruct);
+           leave_crit ();
 
            /* Change the message type to prevent Windows from
               combining WM_PAINT messages in the Lisp thread's queue,
               since Windows assumes that each message queue is
               dedicated to one frame and does not bother checking
               that hwnd matches before combining them.  */
-            my_post_msg (&wmsg, hwnd, WM_EMACS_PAINT, wParam, lParam);
+           my_post_msg (&wmsg, hwnd, WM_EMACS_PAINT, wParam, lParam);
 
-            return 0;
-          }
+           return 0;
+         }
 
        /* If GetUpdateRect returns 0 (meaning there is no update
-           region), assume the whole window needs to be repainted.  */
+          region), assume the whole window needs to be repainted.  */
        GetClientRect (hwnd, &wmsg.rect);
        my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
-        return 0;
+       return 0;
       }
 
     case WM_INPUTLANGCHANGE:
       /* Inform lisp thread of keyboard layout changes.  */
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
 
+      /* The state of the finite automaton is separate per every input
+        language environment (so it does not change when one switches
+        to a different window with the same environment).  Moreover,
+        the experiments show that the state is not remembered when
+        one switches back to the pre-previous environment.  */
+      after_deadkey = -1;
+
+      /* XXXX??? What follows is a COMPLETE misunderstanding of Windows!  */
+
       /* Clear dead keys in the keyboard state; for simplicity only
-         preserve modifier key states.  */
+        preserve modifier key states.  */
       {
        int i;
        BYTE keystate[256];
@@ -3011,7 +3421,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       /* Synchronize modifiers with current keystroke.  */
       sync_modifiers ();
       record_keydown (wParam, lParam);
-      wParam = map_keypad_keys (wParam, (lParam & 0x1000000L) != 0);
 
       windows_translate = 0;
 
@@ -3052,14 +3461,14 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
          if (!NILP (Vw32_rwindow_modifier))
            return 0;
          break;
-       case VK_APPS:
+       case VK_APPS:
          if (!NILP (Vw32_apps_modifier))
            return 0;
          break;
        case VK_MENU:
          if (NILP (Vw32_pass_alt_to_system))
            /* Prevent DefWindowProc from activating the menu bar if an
-               Alt key is pressed and released by itself.  */
+              Alt key is pressed and released by itself.  */
            return 0;
          windows_translate = 1;
          break;
@@ -3083,9 +3492,9 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
          break;
        disable_lock_key:
          /* Ensure the appropriate lock key state (and indicator light)
-             remains in the same state. We do this by faking another
-             press of the relevant key.  Apparently, this really is the
-             only way to toggle the state of the indicator lights.  */
+            remains in the same state. We do this by faking another
+            press of the relevant key.  Apparently, this really is the
+            only way to toggle the state of the indicator lights.  */
          dpyinfo->faked_key = wParam;
          keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
                       KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
@@ -3094,8 +3503,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
          keybd_event ((BYTE) wParam, (BYTE) MapVirtualKey (wParam, 0),
                       KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
          /* Ensure indicator lights are updated promptly on Windows 9x
-             (TranslateMessage apparently does this), after forwarding
-             input event.  */
+            (TranslateMessage apparently does this), after forwarding
+            input event.  */
          post_character_message (hwnd, msg, wParam, lParam,
                                  w32_get_key_modifiers (wParam, lParam));
          windows_translate = 1;
@@ -3107,21 +3516,67 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
          break;
        case VK_CANCEL:
          /* Windows maps Ctrl-Pause (aka Ctrl-Break) into VK_CANCEL,
-             which is confusing for purposes of key binding; convert
+            which is confusing for purposes of key binding; convert
             VK_CANCEL events into VK_PAUSE events.  */
          wParam = VK_PAUSE;
          break;
        case VK_PAUSE:
          /* Windows maps Ctrl-NumLock into VK_PAUSE, which is confusing
-             for purposes of key binding; convert these back into
-             VK_NUMLOCK events, at least when we want to see NumLock key
-             presses.  (Note that there is never any possibility that
-             VK_PAUSE with Ctrl really is C-Pause as per above.)  */
+            for purposes of key binding; convert these back into
+            VK_NUMLOCK events, at least when we want to see NumLock key
+            presses.  (Note that there is never any possibility that
+            VK_PAUSE with Ctrl really is C-Pause as per above.)  */
          if (NILP (Vw32_enable_num_lock) && modifier_set (VK_CONTROL))
            wParam = VK_NUMLOCK;
          break;
        default:
-         /* If not defined as a function key, change it to a WM_CHAR message. */
+         if (w32_unicode_gui)
+           {
+             /* If this event generates characters or deadkeys, do
+                not interpret it as a "raw combination of modifiers
+                and keysym".  Hide deadkeys, and use the generated
+                character(s) instead of the keysym.  (Backward
+                compatibility: exceptions for numpad keys generating
+                0-9 . , / * - +, and for extra-Alt combined with a
+                non-Latin char.)
+
+                Try to not report modifiers which have effect on
+                which character or deadkey is generated.
+
+                Example (contrived): if rightAlt-? generates f (on a
+                Cyrillic keyboard layout), and Ctrl, leftAlt do not
+                affect the generated character, one wants to report
+                Ctrl-leftAlt-f if the user presses
+                Ctrl-leftAlt-rightAlt-?.  */
+             int res;
+#if 0
+             /* Some of WM_CHAR may be fed to us directly, some are
+                results of TranslateMessage().  Using 0 as the first
+                argument (in a separate call) might help us
+                distinguish these two cases.
+
+                However, the keypress feeders would most probably
+                expect the "standard" message pump, when
+                TranslateMessage() is called on EVERY KeyDown/Keyup
+                event.  So they may feed us Down-Ctrl Down-FAKE
+                Char-o and expect us to recognize it as Ctrl-o.
+                Using 0 as the first argument would interfere with
+                this.   */
+             deliver_wm_chars (0, hwnd, msg, wParam, lParam, 1);
+#endif
+             /* Processing the generated WM_CHAR messages *WHILE* we
+                handle KEYDOWN/UP event is the best choice, since
+                withoug any fuss, we know all 3 of: scancode, virtual
+                keycode, and expansion.  (Additionally, one knows
+                boundaries of expansion of different keypresses.)  */
+             res = deliver_wm_chars (1, hwnd, msg, wParam, lParam, 1);
+             windows_translate = -( res != 0 );
+             if (res > 0) /* Bound to character(s) or a deadkey */
+               break;
+             /* deliver_wm_chars may make some branches after this vestigal.  */
+           }
+         wParam = map_keypad_keys (wParam, (lParam & 0x1000000L) != 0);
+         /* If not defined as a function key, change it to a WM_CHAR message.  */
          if (wParam > 255 || !lispy_function_keys[wParam])
            {
              DWORD modifiers = construct_console_modifiers ();
@@ -3173,7 +3628,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                          /* Forward asciified character sequence.  */
                          post_character_message
                            (hwnd, WM_CHAR,
-                             (unsigned char) key.uChar.AsciiChar, lParam,
+                            (unsigned char) key.uChar.AsciiChar, lParam,
                             w32_get_key_modifiers (wParam, lParam));
                          w32_kbd_patch_key (&key, w32_keyboard_codepage);
                        }
@@ -3188,6 +3643,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
            }
        }
 
+    if (windows_translate == -1)
+      break;
     translate:
       if (windows_translate)
        {
@@ -3202,73 +3659,73 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_SYSCHAR:
     case WM_CHAR:
       if (wParam > 255 )
-        {
-          W32Msg wmsg;
+       {
+         W32Msg wmsg;
 
-          wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
-          signal_user_input ();
-          my_post_msg (&wmsg, hwnd, WM_UNICHAR, wParam, lParam);
+         wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
+         signal_user_input ();
+         my_post_msg (&wmsg, hwnd, WM_UNICHAR, wParam, lParam);
 
-        }
+       }
       else
-        post_character_message (hwnd, msg, wParam, lParam,
-                                w32_get_key_modifiers (wParam, lParam));
+       post_character_message (hwnd, msg, wParam, lParam,
+                               w32_get_key_modifiers (wParam, lParam));
       break;
 
     case WM_UNICHAR:
       /* WM_UNICHAR looks promising from the docs, but the exact
-         circumstances in which TranslateMessage sends it is one of those
-         Microsoft secret API things that EU and US courts are supposed
-         to have put a stop to already. Spy++ shows it being sent to Notepad
-         and other MS apps, but never to Emacs.
+        circumstances in which TranslateMessage sends it is one of those
+        Microsoft secret API things that EU and US courts are supposed
+        to have put a stop to already. Spy++ shows it being sent to Notepad
+        and other MS apps, but never to Emacs.
 
-         Some third party IMEs send it in accordance with the official
-         documentation though, so handle it here.
+        Some third party IMEs send it in accordance with the official
+        documentation though, so handle it here.
 
-         UNICODE_NOCHAR is used to test for support for this message.
-         TRUE indicates that the message is supported.  */
+        UNICODE_NOCHAR is used to test for support for this message.
+        TRUE indicates that the message is supported.  */
       if (wParam == UNICODE_NOCHAR)
-        return TRUE;
+       return TRUE;
 
       {
-        W32Msg wmsg;
-        wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
-        signal_user_input ();
-        my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+       W32Msg wmsg;
+       wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
+       signal_user_input ();
+       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
       }
       break;
 
     case WM_IME_CHAR:
       /* If we can't get the IME result as Unicode, use default processing,
-         which will at least allow characters decodable in the system locale
-         get through.  */
+        which will at least allow characters decodable in the system locale
+        get through.  */
       if (!get_composition_string_fn)
-        goto dflt;
+       goto dflt;
 
       else if (!ignore_ime_char)
-        {
-          wchar_t * buffer;
-          int size, i;
-          W32Msg wmsg;
-          HIMC context = get_ime_context_fn (hwnd);
-          wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
-          /* Get buffer size.  */
-          size = get_composition_string_fn (context, GCS_RESULTSTR, NULL, 0);
-          buffer = alloca (size);
-          size = get_composition_string_fn (context, GCS_RESULTSTR,
-                                            buffer, size);
+       {
+         wchar_t * buffer;
+         int size, i;
+         W32Msg wmsg;
+         HIMC context = get_ime_context_fn (hwnd);
+         wmsg.dwModifiers = w32_get_key_modifiers (wParam, lParam);
+         /* Get buffer size.  */
+         size = get_composition_string_fn (context, GCS_RESULTSTR, NULL, 0);
+         buffer = alloca (size);
+         size = get_composition_string_fn (context, GCS_RESULTSTR,
+                                           buffer, size);
          release_ime_context_fn (hwnd, context);
 
-          signal_user_input ();
-          for (i = 0; i < size / sizeof (wchar_t); i++)
-            {
-              my_post_msg (&wmsg, hwnd, WM_UNICHAR, (WPARAM) buffer[i],
-                           lParam);
-            }
-          /* Ignore the messages for the rest of the
+         signal_user_input ();
+         for (i = 0; i < size / sizeof (wchar_t); i++)
+           {
+             my_post_msg (&wmsg, hwnd, WM_UNICHAR, (WPARAM) buffer[i],
+                          lParam);
+           }
+         /* Ignore the messages for the rest of the
             characters in the string that was output above.  */
-          ignore_ime_char = (size / sizeof (wchar_t)) - 1;
-        }
+         ignore_ime_char = (size / sizeof (wchar_t)) - 1;
+       }
       else
        ignore_ime_char--;
 
@@ -3490,7 +3947,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       signal_user_input ();
 
       /* Need to return true for XBUTTON messages, false for others,
-         to indicate that we processed the message.  */
+        to indicate that we processed the message.  */
       return (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONUP);
 
     case WM_MOUSEMOVE:
@@ -3553,11 +4010,11 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_VSCROLL:
       if (w32_mouse_move_interval <= 0
          || (msg == WM_MOUSEMOVE && button_state == 0))
-       {
+       {
          wmsg.dwModifiers = w32_get_modifiers ();
          my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
          return 0;
-       }
+       }
 
       /* Hang onto mouse move and scroll messages for a bit, to avoid
         sending such events to Emacs faster than it can process them.
@@ -3588,15 +4045,15 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
     case WM_APPCOMMAND:
       if (w32_pass_multimedia_buttons_to_system)
-        goto dflt;
+       goto dflt;
       /* Otherwise, pass to lisp, the same way we do with mousehwheel.  */
     case WM_MOUSEHWHEEL:
       wmsg.dwModifiers = w32_get_modifiers ();
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
       signal_user_input ();
       /* Non-zero must be returned when WM_MOUSEHWHEEL messages are
-         handled, to prevent the system trying to handle it by faking
-         scroll bar events.  */
+        handled, to prevent the system trying to handle it by faking
+        scroll bar events.  */
       return 1;
 
     case WM_TIMER:
@@ -3627,15 +4084,15 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
          KillTimer (hwnd, menu_free_timer);
          menu_free_timer = 0;
          f = x_window_to_frame (dpyinfo, hwnd);
-          /* If a popup menu is active, don't wipe its strings.  */
+         /* If a popup menu is active, don't wipe its strings.  */
          if (menubar_in_use
-              && current_popup_menu == NULL)
+             && current_popup_menu == NULL)
            {
              /* Free memory used by owner-drawn and help-echo strings.  */
              w32_free_menu_strings (hwnd);
              if (f)
                f->output_data.w32->menubar_active = 0;
-              menubar_in_use = 0;
+             menubar_in_use = 0;
            }
        }
       return 0;
@@ -3687,7 +4144,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
        if (find_deferred_msg (hwnd, msg) != NULL)
          emacs_abort ();
 
-        menubar_in_use = 1;
+       menubar_in_use = 1;
 
        return send_deferred_msg (&msg_buf, hwnd, msg, wParam, lParam);
       }
@@ -3698,8 +4155,8 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       /* If a menu is still active, check again after a short delay,
         since Windows often (always?) sends the WM_EXITMENULOOP
         before the corresponding WM_COMMAND message.
-         Don't do this if a popup menu is active, since it is only
-         menubar menus that require cleaning up in this way.
+        Don't do this if a popup menu is active, since it is only
+        menubar menus that require cleaning up in this way.
       */
       if (f && menubar_in_use && current_popup_menu == NULL)
        menu_free_timer = SetTimer (hwnd, MENU_FREE_ID, MENU_FREE_DELAY, NULL);
@@ -3744,9 +4201,9 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
              menu_font = CreateFontIndirect (&menu_logfont);
              old_font = SelectObject (hdc, menu_font);
 
-              pMis->itemHeight = GetSystemMetrics (SM_CYMENUSIZE);
-              if (title)
-                {
+             pMis->itemHeight = GetSystemMetrics (SM_CYMENUSIZE);
+             if (title)
+               {
                  if (unicode_append_menu)
                    GetTextExtentPoint32W (hdc, (WCHAR *) title,
                                           wcslen ((WCHAR *) title),
@@ -3754,12 +4211,12 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                  else
                    GetTextExtentPoint32 (hdc, title, strlen (title), &size);
 
-                  pMis->itemWidth = size.cx;
-                  if (pMis->itemHeight < size.cy)
-                    pMis->itemHeight = size.cy;
-                }
-              else
-                pMis->itemWidth = 0;
+                 pMis->itemWidth = size.cx;
+                 if (pMis->itemHeight < size.cy)
+                   pMis->itemHeight = size.cy;
+               }
+             else
+               pMis->itemWidth = 0;
 
              SelectObject (hdc, old_font);
              DeleteObject (menu_font);
@@ -3779,17 +4236,17 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
            {
              /* Draw popup menu title. */
              char * title = (char *) pDis->itemData;
-              if (title)
-                {
-                  HDC hdc = pDis->hDC;
-                  HFONT menu_font = GetCurrentObject (hdc, OBJ_FONT);
-                  LOGFONT menu_logfont;
-                  HFONT old_font;
-
-                  GetObject (menu_font, sizeof (menu_logfont), &menu_logfont);
-                  menu_logfont.lfWeight = FW_BOLD;
-                  menu_font = CreateFontIndirect (&menu_logfont);
-                  old_font = SelectObject (hdc, menu_font);
+             if (title)
+               {
+                 HDC hdc = pDis->hDC;
+                 HFONT menu_font = GetCurrentObject (hdc, OBJ_FONT);
+                 LOGFONT menu_logfont;
+                 HFONT old_font;
+
+                 GetObject (menu_font, sizeof (menu_logfont), &menu_logfont);
+                 menu_logfont.lfWeight = FW_BOLD;
+                 menu_font = CreateFontIndirect (&menu_logfont);
+                 old_font = SelectObject (hdc, menu_font);
 
                  /* Always draw title as if not selected.  */
                  if (unicode_append_menu)
@@ -3808,9 +4265,9 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
                                ETO_OPAQUE, &pDis->rcItem,
                                title, strlen (title), NULL);
 
-                  SelectObject (hdc, old_font);
-                  DeleteObject (menu_font);
-                }
+                 SelectObject (hdc, old_font);
+                 DeleteObject (menu_font);
+               }
              return TRUE;
            }
        }
@@ -3891,16 +4348,16 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
       /* Don't restrict the sizing of any kind of frames.  If the window
         manager doesn't, there's no reason to do it ourselves.  */
 #if 0
-        if (frame_resize_pixelwise || hwnd == tip_window)
+       if (frame_resize_pixelwise || hwnd == tip_window)
 #endif
          return 0;
 
 #if 0
       /* Don't restrict the sizing of fullscreened frames, allowing them to be
-         flush with the sides of the screen.  */
+        flush with the sides of the screen.  */
       f = x_window_to_frame (dpyinfo, hwnd);
       if (f && FRAME_PREV_FSMODE (f) != FULLSCREEN_NONE)
-        return 0;
+       return 0;
 
       {
        WINDOWPLACEMENT wp;
@@ -4049,30 +4506,30 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
     case WM_EMACS_BRINGTOTOP:
     case WM_EMACS_SETFOREGROUND:
       {
-        HWND foreground_window;
-        DWORD foreground_thread, retval;
+       HWND foreground_window;
+       DWORD foreground_thread, retval;
 
-        /* On NT 5.0, and apparently Windows 98, it is necessary to
-           attach to the thread that currently has focus in order to
-           pull the focus away from it.  */
-        foreground_window = GetForegroundWindow ();
+       /* On NT 5.0, and apparently Windows 98, it is necessary to
+          attach to the thread that currently has focus in order to
+          pull the focus away from it.  */
+       foreground_window = GetForegroundWindow ();
        foreground_thread = GetWindowThreadProcessId (foreground_window, NULL);
-        if (!foreground_window
-            || foreground_thread == GetCurrentThreadId ()
-            || !AttachThreadInput (GetCurrentThreadId (),
-                                   foreground_thread, TRUE))
-          foreground_thread = 0;
-
-        retval = SetForegroundWindow ((HWND) wParam);
-        if (msg == WM_EMACS_BRINGTOTOP)
-          retval = BringWindowToTop ((HWND) wParam);
-
-        /* Detach from the previous foreground thread.  */
-        if (foreground_thread)
-          AttachThreadInput (GetCurrentThreadId (),
-                             foreground_thread, FALSE);
-
-        return retval;
+       if (!foreground_window
+           || foreground_thread == GetCurrentThreadId ()
+           || !AttachThreadInput (GetCurrentThreadId (),
+                                  foreground_thread, TRUE))
+         foreground_thread = 0;
+
+       retval = SetForegroundWindow ((HWND) wParam);
+       if (msg == WM_EMACS_BRINGTOTOP)
+         retval = BringWindowToTop ((HWND) wParam);
+
+       /* Detach from the previous foreground thread.  */
+       if (foreground_thread)
+         AttachThreadInput (GetCurrentThreadId (),
+                            foreground_thread, FALSE);
+
+       return retval;
       }
 
     case WM_EMACS_SETWINDOWPOS:
@@ -4145,7 +4602,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
        button_state = 0;
 
        /* Use menubar_active to indicate that WM_INITMENU is from
-           TrackPopupMenu below, and should be ignored.  */
+          TrackPopupMenu below, and should be ignored.  */
        f = x_window_to_frame (dpyinfo, hwnd);
        if (f)
          f->output_data.w32->menubar_active = 1;
@@ -4452,20 +4909,20 @@ x_default_font_parameter (struct frame *f, Lisp_Object parms)
     {
       int i;
       static char *names[]
-        = { "Courier New-10",
-            "-*-Courier-normal-r-*-*-13-*-*-*-c-*-iso8859-1",
-            "-*-Fixedsys-normal-r-*-*-12-*-*-*-c-*-iso8859-1",
-            "Fixedsys",
-            NULL };
+       = { "Courier New-10",
+           "-*-Courier-normal-r-*-*-13-*-*-*-c-*-iso8859-1",
+           "-*-Fixedsys-normal-r-*-*-12-*-*-*-c-*-iso8859-1",
+           "Fixedsys",
+           NULL };
 
       for (i = 0; names[i]; i++)
-        {
-          font = font_open_by_name (f, build_unibyte_string (names[i]));
-          if (! NILP (font))
-            break;
-        }
+       {
+         font = font_open_by_name (f, build_unibyte_string (names[i]));
+         if (! NILP (font))
+           break;
+       }
       if (NILP (font))
-        error ("No suitable font was found");
+       error ("No suitable font was found");
     }
   else if (!NILP (font_param))
     {
@@ -4573,7 +5030,7 @@ This function is an internal primitive--use `make-frame' instead.  */)
 
   fset_icon_name
     (f, x_get_arg (dpyinfo, parameters, Qicon_name, "iconName", "Title",
-                   RES_TYPE_STRING));
+                  RES_TYPE_STRING));
   if (! STRINGP (f->icon_name))
     fset_icon_name (f, Qnil);
 
@@ -4797,7 +5254,7 @@ This function is an internal primitive--use `make-frame' instead.  */)
      frame on this terminal.  */
   if (FRAME_HAS_MINIBUF_P (f)
       && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
-          || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
+         || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
     kset_default_minibuffer_frame (kb, frame);
 
   /* All remaining specified parameters, which have not been "used"
@@ -5522,7 +5979,7 @@ If TERMINAL is omitted or nil, that stands for the selected frame's display.  */
 
 \f
 /***********************************************************************
-                           Window properties
+                          Window properties
  ***********************************************************************/
 
 #if 0 /* TODO : port window properties to W32 */
@@ -5658,9 +6115,9 @@ no value of TYPE (always string in the MS Windows case).  */)
  ***********************************************************************/
 
 static Lisp_Object x_create_tip_frame (struct w32_display_info *,
-                                       Lisp_Object, Lisp_Object);
+                                      Lisp_Object, Lisp_Object);
 static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object,
-                            Lisp_Object, int, int, int *, int *);
+                           Lisp_Object, int, int, int *, int *);
 
 /* The frame of a currently visible tooltip.  */
 
@@ -5868,7 +6325,7 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
                       "cursorType", "CursorType", RES_TYPE_SYMBOL);
   /* Process alpha here (Bug#17344).  */
   x_default_parameter (f, parms, Qalpha, Qnil,
-                       "alpha", "Alpha", RES_TYPE_NUMBER);
+                      "alpha", "Alpha", RES_TYPE_NUMBER);
 
   /* Dimensions, especially FRAME_LINES (f), must be done via
      change_frame_size.  Change will not be effected unless different
@@ -6766,25 +7223,25 @@ value of DIR as in previous invocations; this is standard Windows behavior.  */)
 
     if (file_opened)
       {
-        /* Get an Emacs string from the value Windows gave us.  */
+       /* Get an Emacs string from the value Windows gave us.  */
 #ifdef NTGUI_UNICODE
-        filename = from_unicode_buffer (filename_buf_w);
+       filename = from_unicode_buffer (filename_buf_w);
 #else /* !NTGUI_UNICODE */
        if (use_unicode)
          filename_from_utf16 (filename_buf_w, fname_ret);
        else
          filename_from_ansi (filename_buf_a, fname_ret);
        dostounix_filename (fname_ret);
-        filename = DECODE_FILE (build_unibyte_string (fname_ret));
+       filename = DECODE_FILE (build_unibyte_string (fname_ret));
 #endif /* NTGUI_UNICODE */
 
 #ifdef CYGWIN
-        filename = Fcygwin_convert_file_name_from_windows (filename, Qt);
+       filename = Fcygwin_convert_file_name_from_windows (filename, Qt);
 #endif /* CYGWIN */
 
-        /* Strip the dummy filename off the end of the string if we
-           added it to select a directory.  */
-        if ((use_unicode && file_details_w->nFilterIndex == 2)
+       /* Strip the dummy filename off the end of the string if we
+          added it to select a directory.  */
+       if ((use_unicode && file_details_w->nFilterIndex == 2)
 #ifndef NTGUI_UNICODE
            || (!use_unicode && file_details_a->nFilterIndex == 2)
 #endif
@@ -6797,14 +7254,14 @@ value of DIR as in previous invocations; this is standard Windows behavior.  */)
     /* An error occurred, fallback on reading from the mini-buffer.  */
     else
       filename = Fcompleting_read (
-        orig_prompt,
-        intern ("read-file-name-internal"),
-        orig_dir,
-        mustmatch,
-        orig_dir,
-        Qfile_name_history,
-        default_filename,
-        Qnil);
+       orig_prompt,
+       intern ("read-file-name-internal"),
+       orig_dir,
+       mustmatch,
+       orig_dir,
+       Qfile_name_history,
+       default_filename,
+       Qnil);
 
     UNGCPRO;
   }
@@ -6917,7 +7374,7 @@ DEFUN ("system-move-file-to-trash", Fsystem_move_file_to_trash,
 
 \f
 /***********************************************************************
-                         w32 specialized functions
+                        w32 specialized functions
  ***********************************************************************/
 
 DEFUN ("w32-send-sys-command", Fw32_send_sys_command,
@@ -6952,37 +7409,37 @@ handler application, but typically it is one of the following common
 operations:
 
  \"open\"    - open DOCUMENT, which could be a file, a directory, or an
-               executable program (application).  If it is an application,
-               that application is launched in the current buffer's default
-               directory.  Otherwise, the application associated with
-               DOCUMENT is launched in the buffer's default directory.
+              executable program (application).  If it is an application,
+              that application is launched in the current buffer's default
+              directory.  Otherwise, the application associated with
+              DOCUMENT is launched in the buffer's default directory.
  \"opennew\" - like \"open\", but instruct the application to open
-               DOCUMENT in a new window.
+              DOCUMENT in a new window.
  \"openas\"  - open the \"Open With\" dialog for DOCUMENT.
  \"print\"   - print DOCUMENT, which must be a file.
  \"printto\" - print DOCUMENT, which must be a file, to a specified printer.
-               The printer should be provided in PARAMETERS, see below.
+              The printer should be provided in PARAMETERS, see below.
  \"explore\" - start the Windows Explorer on DOCUMENT.
  \"edit\"    - launch an editor and open DOCUMENT for editing; which
-               editor is launched depends on the association for the
-               specified DOCUMENT.
+              editor is launched depends on the association for the
+              specified DOCUMENT.
  \"find\"    - initiate search starting from DOCUMENT, which must specify
-               a directory.
+              a directory.
  \"delete\"  - move DOCUMENT, a file or a directory, to Recycle Bin.
  \"copy\"    - copy DOCUMENT, which must be a file or a directory, into
-               the clipboard.
+              the clipboard.
  \"cut\"     - move DOCUMENT, a file or a directory, into the clipboard.
  \"paste\"   - paste the file whose name is in the clipboard into DOCUMENT,
-               which must be a directory.
+              which must be a directory.
  \"pastelink\"
-           - create a shortcut in DOCUMENT (which must be a directory)
-               the file or directory whose name is in the clipboard.
+          - create a shortcut in DOCUMENT (which must be a directory)
+              the file or directory whose name is in the clipboard.
  \"runas\"   - run DOCUMENT, which must be an excutable file, with
-               elevated privileges (a.k.a. \"as Administrator\").
+              elevated privileges (a.k.a. \"as Administrator\").
  \"properties\"
-           - open the property sheet dialog for DOCUMENT.
+          - open the property sheet dialog for DOCUMENT.
  nil       - invoke the default OPERATION, or \"open\" if default is
-               not defined or unavailable.
+              not defined or unavailable.
 
 DOCUMENT is typically the name of a document file or a URL, but can
 also be an executable program to run, or a directory to open in the
@@ -7678,7 +8135,7 @@ elements (all size values are in pixels).
                   ? Fcons (make_number (0), make_number (0))
                   : Fcons (make_number (border_width),
                            make_number (border_height)))),
-          Fcons (Qtitle_height,
+          Fcons (Qtitle_height,
                  ((EQ (fullscreen, Qfullboth) || EQ (fullscreen, Qfullscreen))
                   ? make_number (0)
                   : make_number (title_height))),
@@ -8063,14 +8520,14 @@ w32_strerror (int error_no)
     error_no = GetLastError ();
 
   ret = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
-                       FORMAT_MESSAGE_IGNORE_INSERTS,
-                       NULL,
-                       error_no,
-                       0, /* choose most suitable language */
-                       buf, sizeof (buf), NULL);
+                      FORMAT_MESSAGE_IGNORE_INSERTS,
+                      NULL,
+                      error_no,
+                      0, /* choose most suitable language */
+                      buf, sizeof (buf), NULL);
 
   while (ret > 0 && (buf[ret - 1] == '\n' ||
-                     buf[ret - 1] == '\r' ))
+                    buf[ret - 1] == '\r' ))
       --ret;
   buf[ret] = '\0';
   if (!ret)
@@ -8316,7 +8773,7 @@ w32_kbd_patch_key (KEY_EVENT_RECORD *event, int cpId)
   else
     {
       isdead = ToAscii (event->wVirtualKeyCode, event->wVirtualScanCode,
-                        keystate, (LPWORD) ansi_code, 0);
+                       keystate, (LPWORD) ansi_code, 0);
     }
 
   if (isdead == 0)
@@ -8580,8 +9037,8 @@ system to handle them.  */);
   w32_pass_extra_mouse_buttons_to_system = 0;
 
   DEFVAR_BOOL ("w32-pass-multimedia-buttons-to-system",
-               w32_pass_multimedia_buttons_to_system,
-               doc: /* If non-nil, media buttons are passed to Windows.
+              w32_pass_multimedia_buttons_to_system,
+              doc: /* If non-nil, media buttons are passed to Windows.
 Some modern keyboards contain buttons for controlling media players, web
 browsers and other applications.  Generally these buttons are handled on a
 system wide basis, but by setting this to nil they are made available
@@ -8669,12 +9126,12 @@ Chinese, Japanese, and Korean.  */);
   Vx_pixel_size_width_font_regexp = Qnil;
 
   DEFVAR_LISP ("w32-bdf-filename-alist",
-               Vw32_bdf_filename_alist,
-               doc: /* List of bdf fonts and their corresponding filenames.  */);
+              Vw32_bdf_filename_alist,
+              doc: /* List of bdf fonts and their corresponding filenames.  */);
   Vw32_bdf_filename_alist = Qnil;
 
   DEFVAR_BOOL ("w32-strict-fontnames",
-               w32_strict_fontnames,
+              w32_strict_fontnames,
               doc: /* Non-nil means only use fonts that are exact matches for those requested.
 Default is nil, which allows old fontnames that are not XLFD compliant,
 and allows third-party CJK display to work by specifying false charset
@@ -8684,7 +9141,7 @@ fontsets are automatically created.  */);
   w32_strict_fontnames = 0;
 
   DEFVAR_BOOL ("w32-strict-painting",
-               w32_strict_painting,
+              w32_strict_painting,
               doc: /* Non-nil means use strict rules for repainting frames.
 Set this to nil to get the old behavior for repainting; this should
 only be necessary if the default setting causes problems.  */);