From 298b86133dd143722c0395474c0a98e89c0af937 Mon Sep 17 00:00:00 2001
From: Ken Thomases <ken@codeweavers.com>
Date: Tue, 11 Dec 2018 08:30:41 +1100
Subject: [PATCH] winex11: Match keyboard in Unicode

---
 dlls/winex11.drv/keyboard.c | 163 ++++++++++++++++++++++--------------
 1 file changed, 98 insertions(+), 65 deletions(-)

diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c
index 37c96c926f4..95cb4100bc9 100644
--- a/dlls/winex11.drv/keyboard.c
+++ b/dlls/winex11.drv/keyboard.c
@@ -37,6 +37,7 @@
 #include <ctype.h>
 #include <stdarg.h>
 #include <string.h>
+#include <limits.h>
 
 #define NONAMELESSUNION
 
@@ -76,7 +77,7 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
 };
 static CRITICAL_SECTION kbd_section = { &critsect_debug, -1, 0, 0, 0, 0 };
 
-static char KEYBOARD_MapDeadKeysym(KeySym keysym);
+static WCHAR KEYBOARD_MapDeadKeysym(KeySym keysym);
 
 /* Keyboard translation tables */
 #define MAIN_LEN 49
@@ -1417,6 +1418,36 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
     return TRUE;
 }
 
+static WCHAR translate_keysym( Display *display, KeySym keysym )
+{
+    WCHAR ret;
+
+#ifdef HAVE_XKB
+    char buf[16];
+    int count = 0;
+
+    if (use_xkb && (count = XkbTranslateKeySym(display, &keysym, 0, buf, sizeof(buf), NULL)))
+        count = MultiByteToWideChar(CP_UNIXCP, 0, buf, count, &ret, 1);
+
+    if (count != 1)
+#endif
+    {
+        TRACE("XKB could not translate keysym %04lx\n", keysym);
+        /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent
+         * with appropriate ShiftMask and Mode_switch, use XLookupString
+         * to get character in the local encoding.
+         */
+        if (keysym <= 0xFF)
+            ret = keysym;
+        else if (0x01000000 <= keysym && keysym <= 0x0100FFFF)
+            ret = keysym & 0xFFFF;
+        else
+            ret = KEYBOARD_MapDeadKeysym(keysym);
+    }
+
+    return ret;
+}
+
 /**********************************************************************
  *		X11DRV_KEYBOARD_DetectLayout
  *
@@ -1433,8 +1464,8 @@ X11DRV_KEYBOARD_DetectLayout( Display *display )
   KeySym keysym = 0;
   const char (*lkey)[MAIN_LEN][4];
   unsigned max_seq = 0;
-  int max_score = 0, ismatch = 0;
-  char ckey[256][4];
+  int max_score = INT_MIN, ismatch = 0;
+  WCHAR ckey[256][4];
 
   syms = keysyms_per_keycode;
   if (syms > 4) {
@@ -1447,35 +1478,25 @@ X11DRV_KEYBOARD_DetectLayout( Display *display )
       /* get data for keycode from X server */
       for (i = 0; i < syms; i++) {
         if (!(keysym = keycode_to_keysym (display, keyc, i))) continue;
-	/* Allow both one-byte and two-byte national keysyms */
-	if ((keysym < 0x8000) && (keysym != ' '))
-        {
-#ifdef HAVE_XKB
-            if (!use_xkb || !XkbTranslateKeySym(display, &keysym, 0, &ckey[keyc][i], 1, NULL))
-#endif
-            {
-                TRACE("XKB could not translate keysym %04lx\n", keysym);
-                /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent
-                 * with appropriate ShiftMask and Mode_switch, use XLookupString
-                 * to get character in the local encoding.
-                 */
-                ckey[keyc][i] = keysym & 0xFF;
-            }
-        }
-	else {
-	  ckey[keyc][i] = KEYBOARD_MapDeadKeysym(keysym);
-	}
+        ckey[keyc][i] = translate_keysym( display, keysym );
       }
   }
 
   for (current = 0; main_key_tab[current].comment; current++) {
+    DWORD codepage;
+    WCHAR lkeyW[MAIN_LEN][4];
+
     TRACE("Attempting to match against \"%s\"\n", main_key_tab[current].comment);
     match = 0;
     mismatch = 0;
     score = 0;
     seq = 0;
     lkey = main_key_tab[current].key;
+    memset(lkeyW, 0, sizeof(lkeyW));
     pkey = -1;
+    if (!GetLocaleInfoW(main_key_tab[current].lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
+                        (LPWSTR)&codepage, sizeof(codepage)/sizeof(WCHAR)))
+      codepage = CP_ACP;
     for (keyc = min_keycode; keyc <= max_keycode; keyc++) {
       if (ckey[keyc][0]) {
 	/* search for a match in layout table */
@@ -1484,10 +1505,13 @@ X11DRV_KEYBOARD_DetectLayout( Display *display )
 	/* the table, it's okay that the X server has "3#£", for example) */
 	/* however, the score will be higher for longer matches */
 	for (key = 0; key < MAIN_LEN; key++) {
-	  for (ok = 0, i = 0; (ok >= 0) && (i < syms); i++) {
-	    if ((*lkey)[key][i] && ((*lkey)[key][i] == ckey[keyc][i]))
+          if ((*lkey)[key][0] && !lkeyW[key][0])
+            MultiByteToWideChar(codepage, 0, (*lkey)[key], -1, lkeyW[key], 4);
+
+	  for (ok = 0, i = 0; (ok >= 0) && (i < syms) && lkeyW[key][i]; i++) {
+	    if (lkeyW[key][i] == ckey[keyc][i])
 	      ok++;
-	    if ((*lkey)[key][i] && ((*lkey)[key][i] != ckey[keyc][i]))
+	    else
 	      ok = -1;
 	  }
 	  if (ok > 0) {
@@ -1502,11 +1526,7 @@ X11DRV_KEYBOARD_DetectLayout( Display *display )
 	  if (key > pkey) seq++;
 	  pkey = key;
 	} else {
-          /* print spaces instead of \0's */
-          char str[5];
-          for (i = 0; i < 4; i++) str[i] = ckey[keyc][i] ? ckey[keyc][i] : ' ';
-          str[4] = 0;
-          TRACE_(key)("mismatch for keycode %u, got %s\n", keyc, str);
+          TRACE_(key)("mismatch for keycode %u, got %s\n", keyc, debugstr_wn(ckey[keyc], 4));
           mismatch++;
           score -= syms;
 	}
@@ -1613,9 +1633,11 @@ void X11DRV_InitKeyboard( Display *display )
     XKeyEvent e2;
     WORD scan, vkey;
     int keyc, i, keyn, syms;
-    char ckey[4]={0,0,0,0};
+    WCHAR ckey[4] = { 0 };
     const char (*lkey)[MAIN_LEN][4];
+    WCHAR lkeyW[MAIN_LEN][4];
     char vkey_used[256] = { 0 };
+    DWORD codepage;
 
     /* Ranges of OEM, function key, and character virtual key codes.
      * Don't include those handled specially in X11DRV_ToUnicodeEx and
@@ -1672,7 +1694,11 @@ void X11DRV_InitKeyboard( Display *display )
     /* Detect the keyboard layout */
     X11DRV_KEYBOARD_DetectLayout( display );
     lkey = main_key_tab[kbd_layout].key;
+    memset(lkeyW, 0, sizeof(lkeyW));
     syms = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode;
+    if (!GetLocaleInfoW(main_key_tab[kbd_layout].lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
+                        (LPWSTR)&codepage, sizeof(codepage)/sizeof(WCHAR)))
+      codepage = CP_ACP;
 
     /* Now build two conversion arrays :
      * keycode -> vkey + scancode + extended
@@ -1713,26 +1739,14 @@ void X11DRV_InitKeyboard( Display *display )
 	      int maxlen=0,maxval=-1,ok;
 	      for (i=0; i<syms; i++) {
 		keysym = keycode_to_keysym(display, keyc, i);
-		if ((keysym<0x8000) && (keysym!=' '))
-                {
-#ifdef HAVE_XKB
-                    if (!use_xkb || !XkbTranslateKeySym(display, &keysym, 0, &ckey[i], 1, NULL))
-#endif
-                    {
-                        /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent
-                         * with appropriate ShiftMask and Mode_switch, use XLookupString
-                         * to get character in the local encoding.
-                         */
-                        ckey[i] = (keysym <= 0x7F) ? keysym : 0;
-                    }
-		} else {
-		  ckey[i] = KEYBOARD_MapDeadKeysym(keysym);
-		}
+		ckey[i] = translate_keysym(display, keysym);
 	      }
 	      /* find key with longest match streak */
 	      for (keyn=0; keyn<MAIN_LEN; keyn++) {
-		for (ok=(*lkey)[keyn][i=0]; ok&&(i<4); i++)
-		  if ((*lkey)[keyn][i] && (*lkey)[keyn][i]!=ckey[i]) ok=0;
+	        if ((*lkey)[keyn][0] && !lkeyW[keyn][0])
+	          MultiByteToWideChar(codepage, 0, (*lkey)[keyn], -1, lkeyW[keyn], 4);
+		for (ok=lkeyW[keyn][i=0]; ok&&(i<4); i++)
+		  if (lkeyW[keyn][i] && lkeyW[keyn][i]!=ckey[i]) ok=0;
 		if (!ok) i--; /* we overshot */
 		if (ok||(i>maxlen)) {
 		  maxlen=i; maxval=keyn;
@@ -2350,7 +2364,7 @@ INT CDECL X11DRV_GetKeyNameText(LONG lParam, LPWSTR lpBuffer, INT nSize)
 /***********************************************************************
  *		X11DRV_KEYBOARD_MapDeadKeysym
  */
-static char KEYBOARD_MapDeadKeysym(KeySym keysym)
+static WCHAR KEYBOARD_MapDeadKeysym(KeySym keysym)
 {
 	switch (keysym)
 	    {
@@ -2360,65 +2374,84 @@ static char KEYBOARD_MapDeadKeysym(KeySym keysym)
 #endif
 	    case 0x1000FE7E : /* Xfree's XK_Dtilde */
 		return '~';	/* '? */
+
 #ifdef XK_dead_acute
 	    case XK_dead_acute :
 #endif
 	    case 0x1000FE27 : /* Xfree's XK_Dacute_accent */
-		return 0xb4;	/* '' */
+		return 0x00b4;	/* '' */
+
 #ifdef XK_dead_circumflex
 	    case XK_dead_circumflex:
 #endif
 	    case 0x1000FE5E : /* Xfree's XK_Dcircumflex_accent */
 		return '^';	/* '> */
+
 #ifdef XK_dead_grave
 	    case XK_dead_grave :
 #endif
 	    case 0x1000FE60 : /* Xfree's XK_Dgrave_accent */
 		return '`';	/* '! */
+
 #ifdef XK_dead_diaeresis
 	    case XK_dead_diaeresis :
 #endif
 	    case 0x1000FE22 : /* Xfree's XK_Ddiaeresis */
-		return 0xa8;	/* ': */
+		return 0x00a8;	/* ': */
+
 #ifdef XK_dead_cedilla
 	    case XK_dead_cedilla :
-	        return 0xb8;	/* ', */
+	        return 0x00b8;	/* ', */
 #endif
+
 #ifdef XK_dead_macron
 	    case XK_dead_macron :
-	        return '-';	/* 'm isn't defined on iso-8859-x */
+	        return 0x00af;	/* 'm */
 #endif
+
 #ifdef XK_dead_breve
 	    case XK_dead_breve :
-	        return 0xa2;	/* '( */
+	        return 0x02d8;	/* '( */
 #endif
+
 #ifdef XK_dead_abovedot
 	    case XK_dead_abovedot :
-	        return 0xff;	/* '. */
+	        return 0x02d9;	/* '. */
 #endif
+
 #ifdef XK_dead_abovering
 	    case XK_dead_abovering :
-	        return '0';	/* '0 isn't defined on iso-8859-x */
+	        return 0x02da;	/* '0 */
 #endif
+
 #ifdef XK_dead_doubleacute
 	    case XK_dead_doubleacute :
-	        return 0xbd;	/* '" */
+	        return 0x02dd;	/* '" */
 #endif
+
 #ifdef XK_dead_caron
 	    case XK_dead_caron :
-	        return 0xb7;	/* '< */
+	        return 0x02c7;	/* '< */
 #endif
+
 #ifdef XK_dead_ogonek
 	    case XK_dead_ogonek :
-	        return 0xb2;	/* '; */
+	        return 0x02db;	/* '; */
 #endif
-/* FIXME: I don't know this three.
-	    case XK_dead_iota :
-	        return 'i';
+
+#ifdef XK_dead_voiced_sound
 	    case XK_dead_voiced_sound :
-	        return 'v';
+	        return 0x309b;	/* unknown */
+#endif
+
+#ifdef XK_dead_semivoiced_sound
 	    case XK_dead_semivoiced_sound :
-	        return 's';
+	        return 0x309c;	/* unknown */
+#endif
+
+/* FIXME: I don't know this one.
+	    case XK_dead_iota :
+	        return 'i';
 */
 	    }
 	TRACE("no character for dead keysym 0x%08lx\n",keysym);
@@ -2603,7 +2636,7 @@ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState
 
     if (ret == 0)
     {
-	char dead_char;
+	WCHAR dead_char;
 
 #ifdef XK_EuroSign
         /* An ugly hack for EuroSign: X can't translate it to a character
@@ -2627,7 +2660,7 @@ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState
 	dead_char = KEYBOARD_MapDeadKeysym(keysym);
 	if (dead_char)
         {
-	    MultiByteToWideChar(CP_UNIXCP, 0, &dead_char, 1, bufW, bufW_size);
+	    bufW[0] = dead_char;
 	    ret = -1;
             goto found;
         }
-- 
2.24.1

