changeset 7649:4d97a97495bb v7.4.1124

commit https://github.com/vim/vim/commit/25b2b94ea73eff2aeef624d2ba7f59a1a265a0c1 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 17 20:53:12 2016 +0100 patch 7.4.1124 Problem: MS-Windows: dead key behavior is not ideal. Solution: Handle dead keys differently when not in Insert or Select mode. (John Wellesz, closes https://github.com/vim/vim/issues/399)
author Christian Brabandt <cb@256bit.org>
date Sun, 17 Jan 2016 21:00:05 +0100
parents bfc4a1d7725c
children fbc4cabe6704
files src/gui_w48.c src/version.c
diffstat 2 files changed, 92 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/src/gui_w48.c
+++ b/src/gui_w48.c
@@ -301,18 +301,18 @@ static struct
 };
 
 /* Local variables */
-static int		s_button_pending = -1;
+static int	s_button_pending = -1;
 
 /* s_getting_focus is set when we got focus but didn't see mouse-up event yet,
  * so don't reset s_button_pending. */
-static int		s_getting_focus = FALSE;
-
-static int		s_x_pending;
-static int		s_y_pending;
-static UINT		s_kFlags_pending;
-static UINT		s_wait_timer = 0;   /* Timer for get char from user */
-static int		s_timed_out = FALSE;
-static int		dead_key = 0;	/* 0 - no dead key, 1 - dead key pressed */
+static int	s_getting_focus = FALSE;
+
+static int	s_x_pending;
+static int	s_y_pending;
+static UINT	s_kFlags_pending;
+static UINT	s_wait_timer = 0;   /* Timer for get char from user */
+static int	s_timed_out = FALSE;
+static int	dead_key = 0;	/* 0: no dead key, 1: dead key pressed */
 
 #ifdef WIN3264
 static OSVERSIONINFO os_version;    /* like it says.  Init in gui_mch_init() */
@@ -641,6 +641,8 @@ char_to_string(int ch, char_u *string, i
     int		modifiers;
     int		ch = cch;   /* special keys are negative */
 
+    dead_key = 0;
+
     /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */
 
     /* OK, we have a character key (given by ch) which was entered with the
@@ -1710,6 +1712,34 @@ gui_mch_draw_part_cursor(
     DeleteBrush(hbr);
 }
 
+
+/*
+ * Generates a VK_SPACE when the internal dead_key flag is set to output the
+ * dead key's nominal character and re-post the original message.
+ */
+    static void
+outputDeadKey_rePost(MSG originalMsg)
+{
+    static MSG deadCharExpel;
+
+    if (!dead_key)
+	return;
+
+    dead_key = 0;
+
+    /* Make Windows generate the dead key's character */
+    deadCharExpel.message = originalMsg.message;
+    deadCharExpel.hwnd    = originalMsg.hwnd;
+    deadCharExpel.wParam  = VK_SPACE;
+
+    MyTranslateMessage(&deadCharExpel);
+
+    /* re-generate the current character free of the dead char influence */
+    PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam,
+							  originalMsg.lParam);
+}
+
+
 /*
  * Process a single Windows message.
  * If one is not available we hang until one is.
@@ -1790,21 +1820,48 @@ process_message(void)
     if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
     {
 	vk = (int) msg.wParam;
+
 	/*
-	 * If a dead key was pressed and the user presses VK_SPACE, VK_BACK, or
-	 * VK_ESCAPE it means that he actually wants to deal with the dead char
-	 * now, so do nothing special and let Windows handle it.
+	 * Handle dead keys in special conditions in other cases we let Windows
+	 * handle them and do not interfere.
+	 *
+	 * The dead_key flag must be reset on several occasions:
+	 * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily
+	 *   consumed at that point (This is when we let Windows combine the
+	 *   dead character on its own)
 	 *
-	 * Note that VK_SPACE combines with the dead_key's character and only
-	 * one WM_CHAR will be generated by TranslateMessage(), in the two
-	 * other cases two WM_CHAR will be generated: the dead char and VK_BACK
-	 * or VK_ESCAPE.  That is most likely what the user expects.
+	 * - Before doing something special such as regenerating keypresses to
+	 *   expel the dead character as this could trigger an infinite loop if
+	 *   for some reason MyTranslateMessage() do not trigger a call
+	 *   immediately to _OnChar() (or _OnSysChar()).
 	 */
-	if (dead_key && (vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
+	if (dead_key)
 	{
-	    dead_key = 0;
-	    MyTranslateMessage(&msg);
-	    return;
+	    /*
+	     * If a dead key was pressed and the user presses VK_SPACE,
+	     * VK_BACK, or VK_ESCAPE it means that he actually wants to deal
+	     * with the dead char now, so do nothing special and let Windows
+	     * handle it.
+	     *
+	     * Note that VK_SPACE combines with the dead_key's character and
+	     * only one WM_CHAR will be generated by TranslateMessage(), in
+	     * the two other cases two WM_CHAR will be generated: the dead
+	     * char and VK_BACK or VK_ESCAPE. That is most likely what the
+	     * user expects.
+	     */
+	    if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE))
+	    {
+		dead_key = 0;
+		MyTranslateMessage(&msg);
+		return;
+	    }
+	    /* In modes where we are not typing, dead keys should behave
+	     * normally */
+	    else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE)))
+	    {
+		outputDeadKey_rePost(msg);
+		return;
+	    }
 	}
 
 	/* Check for CTRL-BREAK */
@@ -1822,6 +1879,19 @@ process_message(void)
 	    if (special_keys[i].key_sym == vk
 		    && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000)))
 	    {
+		/*
+		 * Behave as exected if we have a dead key and the special key
+		 * is a key that would normally trigger the dead key nominal
+		 * character output (such as a NUMPAD printable character or
+		 * the TAB key, etc...).
+		 */
+		if (dead_key && (special_keys[i].vim_code0 == 'K'
+						|| vk == VK_TAB || vk == CAR))
+		{
+		    outputDeadKey_rePost(msg);
+		    return;
+		}
+
 #ifdef FEAT_MENU
 		/* Check for <F10>: Windows selects the menu.  When <F10> is
 		 * mapped we want to use the mapping instead. */
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1124,
+/**/
     1123,
 /**/
     1122,