Keyboard+Ukrainian

Oleh R. Nykyforchyn nick at pu.if.ua
Thu Jan 13 04:01:13 CST 2005


Dear maintainers!

I propose two patches to Wine20050111 (sorry for not using CVS, but I have rather
slow dialup connection). First is more or less obvious: current cp_20866.c page
includes only Russian KOI8-R codes and is insufficient for Ukrainian and Byelarussian users.
It is easy to add four Ukrainian pairs and one Byelarussian pair of letters (in fact,
move to KOI8-RU). I patched the Perl script an regenerated cp_20866.c:
--- wine-20050111/libs/unicode/c_20866.c.orig	2003-06-26 05:51:12.000000000 +0300
+++ wine-20050111/libs/unicode/c_20866.c	2005-01-09 17:02:22.000000000 +0200
@@ -26,10 +26,10 @@
     0x252c, 0x2534, 0x253c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590,
     0x2591, 0x2592, 0x2593, 0x2320, 0x25a0, 0x2219, 0x221a, 0x2248,
     0x2264, 0x2265, 0x00a0, 0x2321, 0x00b0, 0x00b2, 0x00b7, 0x00f7,
-    0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556,
-    0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e,
-    0x255f, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565,
-    0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x00a9,
+    0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457,
+    0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x0491, 0x045e, 0x255e,
+    0x255f, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407,
+    0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x0490, 0x040e, 0x00a9,
     0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
     0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
     0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
@@ -175,8 +175,8 @@
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     /* 0x0400 .. 0x04ff */
-    0xe5, 0xb3, 0x3f, 0xe7, 0x3f, 0x3f, 0x3f, 0x3f,
-    0x3f, 0x3f, 0x3f, 0x3f, 0xeb, 0xe9, 0xf5, 0x3f,
+    0xe5, 0xb3, 0x3f, 0xe7, 0xb4, 0x3f, 0xb6, 0xb7,
+    0x3f, 0x3f, 0x3f, 0x3f, 0xeb, 0xe9, 0xbe, 0x3f,
     0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa,
     0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0,
     0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe,
@@ -185,15 +185,15 @@
     0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
     0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde,
     0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1,
-    0xc5, 0xa3, 0x3f, 0xc7, 0x3f, 0x3f, 0x3f, 0x3f,
-    0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0xc9, 0xd5, 0x3f,
-    0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+    0xc5, 0xa3, 0x3f, 0xc7, 0xa4, 0x3f, 0xa6, 0xa7,
+    0x3f, 0x3f, 0x3f, 0x3f, 0xcb, 0xc9, 0xae, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+    0xbd, 0xad, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
     0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
--- wine-20050111/libs/unicode/cpmap.pl.orig	2004-01-21 00:39:06.000000000 +0200
+++ wine-20050111/libs/unicode/cpmap.pl	2005-01-12 23:54:42.000000000 +0200
@@ -396,6 +396,58 @@
 
 
 ################################################################
+# parse the input file
+sub READ_KOI_FILE
+{
+    my $name = shift;
+    open INPUT,$name or die "Cannot open $name";
+    @cp2uni = ();
+    @lead_bytes = ();
+    @uni2cp = ();
+
+    while (<INPUT>)
+    {
+        next if /^\#/;  # skip comments
+        next if /^$/;  # skip empty lines
+        next if /\x1a/;  # skip ^Z
+        next if (/^0x([0-9a-fA-F]+)\s+\#UNDEFINED/);  # undefined char
+
+        if (/^0x([0-9a-fA-F]+)\s+\#DBCS LEAD BYTE/)
+        {
+            $cp = hex $1;
+            push @lead_bytes,$cp;
+            $cp2uni[$cp] = 0;
+            next;
+        }
+        if (/^0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\#.*)?/)
+        {
+            $cp = hex $1;
+            $uni = hex $2;
+            $cp2uni[$cp] = $uni unless defined($cp2uni[$cp]);
+            $uni2cp[$uni] = $cp unless defined($uni2cp[$uni]);
+            next;
+        }
+        die "$name: Unrecognized line $_\n";
+    }
+# ukrainian ie/IE
+    @cp2uni[0xa4] = 0x454; @uni2cp[0x454] = 0xa4;
+    @cp2uni[0xb4] = 0x404; @uni2cp[0x404] = 0xb4;
+# ukrainian i/I
+    @cp2uni[0xa6] = 0x456; @uni2cp[0x456] = 0xa6;
+    @cp2uni[0xb6] = 0x406; @uni2cp[0x406] = 0xb6;
+# ukrainian yi/YI
+    @cp2uni[0xa7] = 0x457; @uni2cp[0x457] = 0xa7;
+    @cp2uni[0xb7] = 0x407; @uni2cp[0x407] = 0xb7;
+# ukrainian ghe/GHE with upturn
+    @cp2uni[0xad] = 0x491; @uni2cp[0x491] = 0xad;
+    @cp2uni[0xbd] = 0x490; @uni2cp[0x490] = 0xbd;
+# byelarussian short u/U
+    @cp2uni[0xae] = 0x45e; @uni2cp[0x45e] = 0xae;
+    @cp2uni[0xbe] = 0x40e; @uni2cp[0x40e] = 0xbe;
+}
+
+
+################################################################
 # build EUC-JP table from the JIS 0208 file
 # FIXME: for proper EUC-JP we should probably read JIS 0212 too
 # but this would require 3-byte DBCS characters
@@ -1137,6 +1189,7 @@
 
     # symbol codepage file is special
     if ($codepage == 20932) { READ_JIS0208_FILE($MAPPREFIX . $filename); }
+    elsif ($codepage == 20866) { READ_KOI_FILE($MAPPREFIX . $filename); }
     else { READ_FILE($MAPPREFIX . $filename); }
 
     # hack: 0x00a5 must map to backslash in Shift-JIS

Second proposal is more complicated. I have four XKB groups in my X.org
(Latin + 3 cyrillic encodings), and, moreover, use overlays to switch
between Ukrainian and Russian. This approach is typical among Cyrillic
Linux users. Of course, wine cannot handle my case, so I partially rewrote 
keyboard.c to take care of groups and overlays switching if XKB is present.
Now You can have four independent layouts up to four shift levels in each.
If XKB is absent, wine will behave as before. Driver will switch internal
XKB group counter each time when looked up symbol doesn't match previously
built tables. If this doesn't help, the whole layout ONLY FOR THIS GROUP
(to save time) will be refreshed.

For those who prefer old behaviour or don't believe in radical changes
I add "#ifdef XKB_EXP" everywhere where changes were made. To cancel them,
just undefine this macro. I also added two keyboard layouts (Ukrainian
and Russian) because present Cyrillic ones are rather ancient and don't
match today's de facto standards.

If ChangeLog can be written something like:
  * libs/unicode/cpmap.pl, libs/unicode/cp_20866:
    Oleh R. Nykyforchyn <nick at pu.if.ua>
    added four Ukrainian pairs and one Byelarussian pair of letters to
    extend KOI8-R to KOI8-RU

  * /dlls/x11drv/keyboard.c
    Oleh R. Nykyforchyn <nick at pu.if.ua>
    keyboard.c partially rewritten to take care of groups and overlays 
    switching if XKB is present. Now You can have four independent layouts
    up to four shift levels in each. If XKB is absent or macro XKB_EXP at 
    the beginning of the file is undefined, wine behaves as before.

Here is the thing (to the end of file).

Sincerely Yours
Oleh Nykyforchyn

--- wine-20050111/dlls/x11drv/keyboard.c.orig	2005-01-03 16:44:27.000000000 +0200
+++ wine-20050111/dlls/x11drv/keyboard.c	2005-01-13 11:07:50.000000000 +0200
@@ -51,12 +51,27 @@
 #include "wine/unicode.h"
 #include "wine/debug.h"
 
+#define XKB_EXP /* EXPERIMENTAL */
+/* undefine if You miss old behaviour */
+
 WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
 WINE_DECLARE_DEBUG_CHANNEL(key);
 WINE_DECLARE_DEBUG_CHANNEL(dinput);
 
 static int min_keycode, max_keycode, keysyms_per_keycode;
+
+#ifdef XKB_EXP
+static int xkb_group = 0;   /* Always if we  */
+static int xkb_overlay = 0; /* don't use XKB */
+static WORD keyc2vkey[4][256] = {{0}}, keyc2scan[4][256] = {{0}};
+static KeySym keyc2ksym[4][256][4] = {{{0}}};
+static char keyc2char[4][256][4] = {{{0}}};
+static unsigned char codes[4][256] = {{0}};
+static KeyCode keyc2over[3][256] = {{0}};
+static KeyCode over2keyc[3][256] = {{0}};
+#else
 static WORD keyc2vkey[256], keyc2scan[256];
+#endif
 
 static LPBYTE pKeyStateTable;
 static int NumLockMask, AltGrMask; /* mask in the XKeyEvent state */
@@ -488,6 +503,27 @@
  "<>" /* the phantom key */
 };
 
+/*** Ukrainian keyboard layout KOI8-U by O. Nykyforchyn */
+/***  (as it appears on most of keyboards sold today)   */
+static const char main_key_UA_std[MAIN_LEN][4] =
+{
+ "ґҐ","1!","2\"","3'","4;","5%","6:","7?","8*","9(","0)","-_","=+",
+ "йЙ","цЦ","уУ","кК","еЕ","нН","гГ","шШ","щЩ","зЗ","хХ","їЇ",
+ "фФ","іІ","вВ","аА","пП","рР","оО","лЛ","дД","жЖ","єЄ","\\/",
+ "яЯ","чЧ","сС","мМ","иИ","тТ","ьЬ","бБ","юЮ",".,",
+ "<>" /* the phantom key */
+};
+
+/*** Russian keyboard layout KOI8-R (pair to the previous) */
+static const char main_key_RU_std[MAIN_LEN][4] =
+{
+ "ёЁ","1!","2\"","3'","4;","5%","6:","7?","8*","9(","0)","-_","=+",
+ "йЙ","цЦ","уУ","кК","еЕ","нН","гГ","шШ","щЩ","зЗ","хХ","ъЪ",
+ "фФ","ыЫ","вВ","аА","пП","рР","оО","лЛ","дД","жЖ","эЭ","\\/",
+ "яЯ","чЧ","сС","мМ","иИ","тТ","ьЬ","бБ","юЮ",".,",
+ "<>" /* the phantom key */
+};
+
 /*** Spanish keyboard layout (setxkbmap es) */
 static const char main_key_ES[MAIN_LEN][4] =
 {
@@ -812,6 +848,8 @@
  {0x0419, "Russian keyboard layout cp1251", &main_key_RU_cp1251, &main_key_scan_qwerty, &main_key_vkey_qwerty},
  {0x0419, "Russian phonetic keyboard layout", &main_key_RU_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
  {0x0422, "Ukrainian keyboard layout KOI8-U", &main_key_UA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0422, "Ukrainian keyboard layout (standard)", &main_key_UA_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0419, "Russian keyboard layout (standard)", &main_key_RU_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
  {0x040a, "Spanish keyboard layout", &main_key_ES, &main_key_scan_qwerty, &main_key_vkey_qwerty},
  {0x0410, "Italian keyboard layout", &main_key_IT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
  {0x040f, "Icelandic keyboard layout", &main_key_IS, &main_key_scan_qwerty, &main_key_vkey_qwerty},
@@ -840,7 +878,13 @@
 
  {0, NULL, NULL, NULL, NULL} /* sentinel */
 };
+
+#ifdef XKB_EXP
+static unsigned kbd_layout[4]= {0}; /* indices into above table of layouts
+                                       for four possible XKB groups */
+#else
 static unsigned kbd_layout=0; /* index into above table of layouts */
+#endif
 
 /* maybe more of these scancodes should be extended? */
                 /* extended must be set for ALT_R, CTRL_R,
@@ -946,17 +990,100 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
 };
 
+#ifdef XKB_EXP
+
+/**********************************************************************
+ *		X11DRV_KEYBOARD_GroupChanged
+ *
+ *  Effective group and overlays are taken into consideration
+ */
+
+static Bool
+X11DRV_KEYBOARD_GroupChanged (Display *display)
+{
+#ifdef HAVE_XKB
+  XkbStateRec state;
+  if (use_xkb) {
+    XkbGetState(display, XkbUseCoreKbd, &state);
+    if (state.locked_group != xkb_group) {
+      xkb_group = state.locked_group;
+      return True;
+    }
+    else {
+      xkb_group = state.locked_group;
+      return False;
+    }
+  }
+#endif
+  xkb_group = 0;
+  return False;
+}
+
+
+static void
+X11DRV_KEYBOARD_RefreshLayout (void);
+
+#endif
+
+/**********************************************************************
+ *              EVENT_event_to_vkey
+ *
+ */
 
 /* Returns the Windows virtual key code associated with the X event <e> */
 /* x11 lock must be held */
 static WORD EVENT_event_to_vkey( XIC xic, XKeyEvent *e)
 {
     KeySym keysym = 0;
+    int savetype;
+    unsigned int savestate;
+
+    TRACE_(key)("Received e->keycode = %x\n", e->keycode);
 
-    if (xic)
-        XmbLookupString(xic, e, NULL, 0, &keysym, NULL);
+    savestate = e->state;
+    if (xic) {
+         /* XmbLookupString returns neither char nor keysym for KeyRelease */
+          savetype = e->type;
+          if (savetype == KeyRelease) e->type = KeyPress;
+          XmbLookupString(xic, e, NULL, 0, &keysym, NULL);
+          e->type = savetype;
+        }
     else
         XLookupString(e, NULL, 0, &keysym, NULL);
+    e->state = savestate;
+
+#ifdef XKB_EXP
+    int checkgroup = 1, checklayout = 1, i;
+    KeyCode kc;
+
+  again:
+    kc = over2keyc[xkb_overlay][e->keycode];
+    TRACE_(key)("Treated as e->keycode = %x\n", kc);
+    if (checkgroup || checklayout) {
+      if (codes[xkb_group][kc])
+       for (i = 0; i < 4; i++) {
+        TRACE_(key)("Compare: 0x%04lx <> 0x%04lx, group %d, overlay %d, level %d\n",
+           keysym,keyc2ksym[xkb_group][kc][i], xkb_group, xkb_overlay, i);
+        if (keysym == keyc2ksym[xkb_group][kc][i]) {
+          checkgroup = checklayout = 0;
+          break;
+        }
+       }
+      else
+        TRACE_(key)("No translated keysyms for keycode %d in group %d\n",
+                     kc, xkb_group);
+      if (checkgroup) {
+         checkgroup = 0;
+         X11DRV_KEYBOARD_GroupChanged(thread_display());
+         goto again;
+      }
+      if (checklayout) {
+         checklayout = 0;
+         X11DRV_KEYBOARD_RefreshLayout ();
+         goto again;
+      }
+    }
+#endif
 
     if ((keysym >= 0xFFAE) && (keysym <= 0xFFB9) && (keysym != 0xFFAF)
 	&& (e->state & NumLockMask))
@@ -964,11 +1091,16 @@
          * depending on the NumLock state */
         return nonchar_key_vkey[keysym & 0xFF];
 
-    TRACE_(key)("e->keycode = %x\n", e->keycode);
-
+#ifdef XKB_EXP
+    e->keycode = kc;
+    return  keyc2vkey[xkb_group][kc];
+#else
     return keyc2vkey[e->keycode];
+#endif
 }
 
+
+
 static BOOL NumState=FALSE, CapsState=FALSE;
 
 
@@ -1077,7 +1209,11 @@
         for (j = 0; j < 8; j++)
         {
             if (!(event->key_vector[i] & (1<<j))) continue;
+#ifdef XKB_EXP
+            switch(keyc2vkey[xkb_group][(i * 8) + j] & 0xff)
+#else
             switch(keyc2vkey[(i * 8) + j] & 0xff)
+#endif
             {
             case VK_MENU:    alt = 1; break;
             case VK_CONTROL: control = 1; break;
@@ -1101,7 +1237,8 @@
     KeySym keysym = 0;
     WORD vkey = 0, bScan;
     DWORD dwFlags;
-    int ascii_chars;
+    int ascii_chars, savetype;
+    unsigned int savestate;
     XIC xic = X11DRV_get_ic( hwnd );
     DWORD event_time = event->time - X11DRV_server_startticks;
     Status status = 0;
@@ -1110,10 +1247,17 @@
 		event->type, event->window, event->state, event->keycode);
 
     wine_tsx11_lock();
-    if (xic)
-        ascii_chars = XmbLookupString(xic, event, Str, sizeof(Str), &keysym, &status);
+    savestate = event->state;
+    if (xic) {
+         /* XmbLookupString returns neither char nor keysym for KeyRelease */
+          savetype = event->type;
+          if (savetype == KeyRelease) event->type = KeyPress;
+          ascii_chars = XmbLookupString(xic, event, Str, sizeof(Str), &keysym, NULL);
+          event->type = savetype;
+        }
     else
         ascii_chars = XLookupString(event, Str, sizeof(Str), &keysym, NULL);
+    event->state = savestate;
     wine_tsx11_unlock();
 
     /* Ignore some unwanted events */
@@ -1158,7 +1302,7 @@
         wine_tsx11_unlock();
 	if (!ksname)
 	  ksname = "No Name";
-	TRACE_(key)("%s : keysym=%lX (%s), # of chars=%d / 0x%02x / '%s'\n",
+	TRACE_(key)("%s : keysym=%lX (%s), # of chars=%d / 0x%02x / '%c'\n",
                     (event->type == KeyPress) ? "KeyPress" : "KeyRelease",
                     keysym, ksname, ascii_chars, Str[0] & 0xff, Str);
     }
@@ -1203,7 +1347,11 @@
 	NumState = FALSE;
 	CapsState = FALSE;
 
+#ifdef XKB_EXP
+	bScan = keyc2scan[xkb_group][event->keycode] & 0xFF;
+#else
 	bScan = keyc2scan[event->keycode] & 0xFF;
+#endif
 	TRACE_(key)("bScan = 0x%02x.\n", bScan);
 
 	dwFlags = 0;
@@ -1215,8 +1363,384 @@
    }
 }
 
+#ifdef XKB_EXP
+
+#ifdef HAVE_XKB
+
+/**********************************************************************
+ *		X11DRV_KEYBOARD_RefreshOverlay
+ *
+ */
+
+static void
+X11DRV_KEYBOARD_RefreshOverlays(XkbDescPtr xkb) { /* should contain server fields */
+
+  KeyCode keyc, over;
+  int i;
+  char betype, bedata, overlaytype;
+
+  if (!use_xkb) {
+    for (over = xkb->min_key_code; (over && (over <= xkb->max_key_code)); over++)
+         keyc2over[0][over] = keyc2over[1][over] = keyc2over[2][over] =
+         over2keyc[0][over] = over2keyc[1][over] = over2keyc[2][over] = over;
+    return;
+  }
+  for (i = 0; i <= 2; i++) {
+    if (i == 1) overlaytype = XkbKB_Overlay1;
+    else  if (i == 2) overlaytype = XkbKB_Overlay2;
+    for (keyc = xkb->min_key_code; (keyc && (keyc <= xkb->max_key_code)); keyc++) {
+        if (i == 0) {
+          keyc2over[0][keyc] = over2keyc[0][keyc] = keyc;
+          continue;
+        }
+        over = keyc;
+/*        TRACE_(key)("Check keycode %d (Overlay%d)\n", keyc, i); */
+    haveoverlay:
+        betype = (xkb->server->behaviors)[over].type;
+        bedata = (xkb->server->behaviors)[over].data;
+        if (betype == overlaytype) {
+/*          TRACE_(key)("    changed to %d\n", bedata); */
+          over = bedata;
+          goto haveoverlay;
+        }
+        keyc2over[i][keyc] = over;
+        if (keyc != over)
+          TRACE_(key)("Keycode %d changed to %d (Overlay%d)\n", keyc, over, i);
+    }
+    if (i == 0) continue;
+    for (over = xkb->min_key_code; (over && (over <= xkb->max_key_code)); over++) {
+      over2keyc[i][over] = over;
+      for (keyc = xkb->min_key_code; keyc < over; keyc++) {
+        if (keyc2over[i][keyc] == keyc2over[i][over]) {
+          over2keyc[i][over] = over2keyc[i][keyc];
+          break;
+        }
+      }
+      if (over2keyc[i][over] != over)
+          TRACE_(key)(" In Overlay%d keycode %d behaves as %d\n",
+                       i, over, over2keyc[i][over]);
+    }
+  }
+}
+#endif /* HAVE_XKB */
+
+/**********************************************************************
+ *		X11DRV_KEYBOARD_CharsForKeycode
+ *
+ */
+
+static Bool
+X11DRV_KEYBOARD_CharsForKeyCode (Display *display,
+#ifdef HAVE_XKB    /* should contain server, map and ctrls fields */
+                                    XkbDescPtr xkb,
+#endif
+                                    KeyCode keyc,
+#ifdef HAVE_XKB
+                                    int group,     /* obvious */
+#endif
+                                    KeySym (*ksym)[4], /*    output     */
+                                    char (*chars)[4],  /* return values */
+                                    int *numchars) {   /*   how many    */
+  char Chars[5];
+  int i, w;
+  (*numchars) = 0;
+  KeySym keysym;
+
+#ifdef HAVE_XKB
+  unsigned info;
+  if (use_xkb) {
+    unsigned ctrls = xkb->ctrls->enabled_ctrls;
+    if       (ctrls & XkbOverlay1Mask) xkb_overlay = 1;
+    else  if (ctrls & XkbOverlay2Mask) xkb_overlay = 2;
+        else xkb_overlay = 0;
+
+    if (xkb_overlay)
+        keyc = keyc2over[xkb_overlay][keyc];
+
+    w = XkbKeyNumGroups(xkb,keyc);
+    if (!w) {
+      TRACE_(key)("No groups for keycode %d\n", keyc);
+      (*numchars) = 0;
+      if (chars) (*chars)[0] = 0;
+      return False;
+    }
+    info = XkbOutOfRangeGroupInfo(XkbKeyGroupInfo(xkb,keyc));
+    if (group>=w)
+      switch (info) {
+        case (XkbRedirectIntoRange): group = XkbOutOfRangeGroupInfo(XkbKeyGroupInfo(xkb,keyc));
+                                   if (group >= w) w = 0; break;
+        case (XkbClampIntoRange): ;  group = w -1; break;
+        default: group %= w; break;
+      }
+    w = XkbKeyGroupWidth(xkb,keyc,group);
+  } else
+#endif
+  w = 4;
+
+  if (ksym != NULL) (*ksym)[0] = (*ksym)[1] = (*ksym)[2] = (*ksym)[3] = NoSymbol;
+  for (i = 0; i < w; i++) {
+#ifdef HAVE_XKB
+    if (use_xkb) if ((keysym=XkbKeySymEntry(xkb, keyc, i, group)) == NoSymbol) break;
+    if (!use_xkb)
+#endif
+	keysym = XKeycodeToKeysym (display, keyc, i);
+    if (ksym != NULL) (*ksym)[i] = keysym;
+    if (keysym == NoSymbol) break;
+    if ((keysym < 0x8000) && (keysym != ' ')) {
+#ifdef HAVE_XKB
+          if ((!use_xkb) || (!XkbTranslateKeySym(display, &keysym, 0, &Chars[i], 1, NULL)))
+#endif
+          {
+                TRACE_(key)("XKB could not translate keysym %ld\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.
+                 */
+                Chars[i] = keysym & 0xFF;
+          }
+    }
+    else {
+      Chars[i] = KEYBOARD_MapDeadKeysym(keysym);
+    }
+    if (!Chars[i]) break;
+    (*chars)[i]=Chars[i];
+    (*numchars)++;
+  }
+  Chars[i]='\0';
+  TRACE_(key)("Keycode %d translated to %d chars: \"%s\"\n", keyc, *numchars, Chars);
+/*  if (ksym)  TRACE_(key)("  Keysyms: %s, %s, %s, %s\n",
+            XKeysymToString((*ksym)[0]), XKeysymToString((*ksym)[1]),
+            XKeysymToString((*ksym)[2]), XKeysymToString((*ksym)[3])); */
+
+  if (*numchars) return True;
+  else return False;
+}
+
+/**********************************************************************
+ *		X11DRV_KEYBOARD_RefreshLayout
+ *
+ *  Effective group and overlays are taken into consideration
+ */
+
+static void
+X11DRV_KEYBOARD_RefreshLayout (void)
+{
+
+  char bkey[4];
+  int current, key, i, ok, have_chars;
+  int score, match, miss, inv;
+  int prev, bestScore, bestInv, bestMiss;
+  WORD scan, vkey;
+
+  const char (*lkey)[MAIN_LEN][4];
+  KeyCode keyc;
+  KeySym keysym;
+
+  Display *display = thread_display();
+  X11DRV_KEYBOARD_GroupChanged (display);
+#ifdef HAVE_XKB
+  XkbDescPtr xkb;
+  if (use_xkb) {
+  xkb = XkbGetKeyboard(display,
+         XkbAllComponentsMask,
+         XkbUseCoreKbd);
+  XkbGetControls(display, XkbAllControlsMask, xkb);
+  min_keycode = xkb->min_key_code;
+  max_keycode = xkb->max_key_code;
+  TRACE("Selected XKB group %d, keycodes from %d to %d\n",
+          xkb_group, min_keycode, max_keycode);
+  X11DRV_KEYBOARD_RefreshOverlays(xkb);
+  } else
+#endif
+  {
+  XDisplayKeycodes(display, &min_keycode, &max_keycode);
+  TRACE("No XKB, keycodes from %d to %d\n",
+          min_keycode, max_keycode);
+  }
+
+  for (key=0; key<256; key++) {
+    keyc2vkey[xkb_group][key] = 0; keyc2scan[xkb_group][key] = 0; codes[xkb_group][key] = 0;
+    for (i=0; i<4; i++) {
+      keyc2ksym[xkb_group][key][i] = NoSymbol; keyc2char[xkb_group][key][i] ='\0';
+    }
+  }
+
+  for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++) {
+     if (!X11DRV_KEYBOARD_CharsForKeyCode (display,
+#ifdef HAVE_XKB
+                                             xkb,
+#endif
+                                             keyc,
+#ifdef HAVE_XKB
+                                             xkb_group,
+#endif
+                                             &keyc2ksym[xkb_group][keyc], &bkey, &have_chars)) {
+#ifdef HAVE_XKB
+       TRACE_(key)("No keysyms translated in the group %d for keycode %d\n", xkb_group, keyc);
+#else
+       TRACE_(key)("No keysyms translated for keycode %d\n", keyc);
+#endif
+       codes[xkb_group][keyc] = 0;
+       continue;
+     }
+     for (i = 0; i < 4; i++) {
+       if (i < have_chars) keyc2char[xkb_group][keyc][i]=bkey[i];
+       if ((!bkey[i]) || (i >= have_chars)) bkey[i]=' ';
+     }
+     codes[xkb_group][keyc] = have_chars;
+  }
+
+  bestScore = -1; bestInv = max_keycode; bestMiss = MAIN_LEN; prev = 0;
+  for (current = 0; main_key_tab[current].comment; current++) {
+       score = match = miss = inv = 0;
+       lkey = main_key_tab[current].key;
+	/* search for a match in layout table */
+	/* right now, we just find an absolute match for defined positions */
+	/* (undefined positions are ignored, so if it's defined as "3#" in */
+	/* the table, it's okay that the X server has "3#ё", for example) */
+	/* however, the score will be higher for longer matches */
+       for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++) {
+         if (!codes[xkb_group][keyc]) continue;
+	 for (key = 0; key < MAIN_LEN; key++) {
+	  for (ok = 0, i = 0; (ok >= 0) && (i < codes[xkb_group][keyc]); i++) {
+	    if ((*lkey)[key][i] && ((*lkey)[key][i] == keyc2char[xkb_group][keyc][i]))
+	      ok++;
+	    if ((*lkey)[key][i] && ((*lkey)[key][i] != keyc2char[xkb_group][keyc][i]))
+	      ok = -1;
+	  }
+	  if (ok > 0) {
+	    score += ok;
+	    break;
+	  }
+	 }
+	/* count the matches and mismatches */
+	 if (ok > 0) {
+	   match++;
+/*           TRACE_(key)("keycode %d matches entry %d\n",
+                      keyc, key); */
+	  /* and how much the keycode order matches */
+	   if (key < prev) inv++;
+	   prev = key;
+	 }
+         else {
+          /* print spaces instead of \0's */
+/*           TRACE_(key)("mismatch for keycode %d\n",
+                      keyc); */
+	   miss++;
+	   score -= codes[xkb_group][keyc];
+	 }
+      }
+      TRACE("\"%s\": matches=%d, mismatches=%d, inversions=%d, score=%d\n",
+           main_key_tab[current].comment,
+           match, miss, inv, score);
+      if ((score > bestScore) ||
+         ((score > bestScore) && (inv<bestInv))) {
+           bestScore  = score;
+           bestInv    = inv;
+           bestMiss   = miss;
+           kbd_layout[xkb_group] = current;
+      }
+  }
+
+#ifdef HAVE_XKB
+  XkbFreeKeyboard(xkb,XkbAllComponentsMask,True);
+#endif
+   
+  if (bestMiss) {
+    TRACE("Detected layout is \"%s\" (closest match)\n",
+                     main_key_tab[kbd_layout[xkb_group]].comment);
+  }
+  else {
+    TRACE("Detected layout is \"%s\" (exact match)\n",
+                     main_key_tab[kbd_layout[xkb_group]].comment);
+  }
+
+  for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+  {
+        keysym = keyc2ksym[xkb_group][keyc][0];
+        have_chars = codes[xkb_group][keyc];
+        vkey = 0; scan = 0;
+        if (keysym)  /* otherwise, keycode not used */
+        {
+            if ((keysym >> 8) == 0xFF)         /* non-character key */
+            {
+                vkey = nonchar_key_vkey[keysym & 0xff];
+                scan = nonchar_key_scan[keysym & 0xff];
+		/* set extended bit when necessary */
+		if (scan & 0x100) vkey |= 0x100;
+            } else if (keysym == 0x20) {                 /* Spacebar */
+	        vkey = VK_SPACE;
+		scan = 0x39;
+	    } else if (have_chars) {
+	      /* we seem to need to search the layout-dependent scancodes */
+	      lkey = main_key_tab[kbd_layout[xkb_group]].key;
+	      int maxlen=0,maxval=-1;
+	      /* find key with longest match streak */
+	      for (key=0; key<MAIN_LEN; key++) {
+		if ((ok=(*lkey)[key][i=0]))
+                 for(; i<4; i++)
+		  if ((*lkey)[key][i] && (*lkey)[key][i]!=keyc2char[xkb_group][keyc][i]) {
+                   ok=0; break;
+                  }
+		if (ok||(i>maxlen)) {
+		  maxlen=i; maxval=key;
+		}
+		if (ok) break;
+	      }
+	      if (maxval>=0) {
+		/* got it */
+		const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout[xkb_group]].scan;
+		const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout[xkb_group]].vkey;
+		scan = (*lscan)[maxval];
+		vkey = (*lvkey)[maxval];
+                unsigned int out[8];
+                out[0] = (unsigned char)keyc2char[xkb_group][keyc][0];
+                out[1] = (unsigned char)keyc2char[xkb_group][keyc][1];
+                out[2] = (unsigned char)keyc2char[xkb_group][keyc][2];
+                out[3] = (unsigned char)keyc2char[xkb_group][keyc][3];
+                out[4] = (unsigned char)(*lkey)[maxval][0];
+                out[5] = (unsigned char)(*lkey)[maxval][1];
+                out[6] = (unsigned char)(*lkey)[maxval][2];
+                out[7] = (unsigned char)(*lkey)[maxval][3];
+                TRACE_(key)("Identified \"%02x,%02x,%02x,%02x\" as \"%02x,%02x,%02x,%02x\", entry %d\n",
+                            out[0], out[1], out[2], out[3],
+                            out[4], out[5], out[6], out[7],
+			    maxval); 
+	      }
+              else codes[xkb_group][keyc] = 0;
+	    }
+#if 0 
+      /* this breaks VK_OEM_x VKeys in some layout tables by inserting
+       * a VK code into a not appropriate place ...
+       * Take from xxxInitKeyboard if You need it
+       */
+#endif /* 0 */
+        }
+        TRACE("keycode %04x => vkey %04x\n", keyc, vkey);
+        keyc2vkey[xkb_group][keyc] = vkey;
+        keyc2scan[xkb_group][keyc] = scan;
+  } /* for */
+
+    /* If some keys still lack scancodes, assign some arbitrary ones to them now */
+  for (scan = 0x60, keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+      if (keyc2vkey[xkb_group][keyc]&&!keyc2scan[xkb_group][keyc]) {
+	char *ksname;
+	keysym = XKeycodeToKeysym(display, keyc, 0);
+	ksname = XKeysymToString(keysym);
+	if (!ksname) ksname = "NoSymbol";
+
+	/* should make sure the scancode is unassigned here, but >=0x60 currently always is */
+
+	TRACE_(key)("assigning scancode %02x to unidentified keycode %02x (%s)\n",scan,keyc,ksname);
+	keyc2scan[xkb_group][keyc]=scan++;
+      }
+}
+
+#else /* without XKB_EXP */
+
 /**********************************************************************
  *		X11DRV_KEYBOARD_DetectLayout
+ *                    ( Old behaviour )
  *
  * Called from X11DRV_InitKeyboard
  *  This routine walks through the defined keyboard layouts and selects
@@ -1325,31 +1849,41 @@
   TRACE("detected layout is \"%s\"\n", main_key_tab[kbd_layout].comment);
 }
 
+#endif /* without XKB_EXP */
+
 /**********************************************************************
  *		InitKeyboard (X11DRV.@)
  */
 void X11DRV_InitKeyboard( BYTE *key_state_table )
 {
     Display *display = thread_display();
-    KeySym *ksp;
     XModifierKeymap *mmp;
-    KeySym keysym;
     KeyCode *kcp;
+    int i;
+#ifndef XKB_EXP
+    KeySym *ksp;
+    KeySym keysym;
     XKeyEvent e2;
     WORD scan, vkey, OEMvkey;
-    int keyc, i, keyn, syms;
+    int keyc, keyn, syms;
     char ckey[4]={0,0,0,0};
     const char (*lkey)[MAIN_LEN][4];
+#endif
 
     pKeyStateTable = key_state_table;
 
     wine_tsx11_lock();
+
+#ifdef XKB_EXP
+    X11DRV_KEYBOARD_RefreshLayout();
+#else
     XDisplayKeycodes(display, &min_keycode, &max_keycode);
     ksp = XGetKeyboardMapping(display, min_keycode,
                               max_keycode + 1 - min_keycode, &keysyms_per_keycode);
     /* We are only interested in keysyms_per_keycode.
        There is no need to hold a local copy of the keysyms table */
     XFree(ksp);
+#endif
 
     mmp = XGetModifierMapping(display);
     kcp = mmp->modifiermap;
@@ -1372,6 +1906,7 @@
     }
     XFreeModifiermap(mmp);
 
+#ifndef XKB_EXP
     /* Detect the keyboard layout */
     X11DRV_KEYBOARD_DetectLayout();
     lkey = main_key_tab[kbd_layout].key;
@@ -1507,7 +2042,7 @@
                     TRACE(")\n");
                 }
             }
-#endif
+#endif /* 0 */
         }
         TRACE("keycode %04x => vkey %04x\n", e2.keycode, vkey);
         keyc2vkey[e2.keycode] = vkey;
@@ -1528,6 +2063,8 @@
 	keyc2scan[keyc]=scan++;
       }
 
+#endif /* XKB_EXP*/
+
     /* Now store one keycode for each modifier. Used to simulate keypresses. */
     kcControl = XKeysymToKeycode(display, XK_Control_L);
     kcAlt = XKeysymToKeycode(display, XK_Alt_L);
@@ -1623,13 +2160,15 @@
     DWORD layout;
     LANGID langid;
 
+#ifdef XKB_EXP
+    layout = main_key_tab[kbd_layout[xkb_group]].lcid;
+#else
     layout = main_key_tab[kbd_layout].lcid;
-    /* see comment for GetKeyboardLayout */
+#endif
+    /* see comment above */
     langid = PRIMARYLANGID(LANGIDFROMLCID(layout));
     if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
         layout |= 0xe001 << 16; /* FIXME */
-    else
-        layout |= layout << 16;
 
     sprintfW(name, formatW, layout);
     TRACE("returning %s\n", debugstr_w(name));
@@ -1728,7 +2267,11 @@
             cChar, keysym, keysym, keycode, keycode);
 
     /* keycode -> (keyc2vkey) vkey */
+#ifdef XKB_EXP
+    ret = keyc2vkey[xkb_group][keycode];
+#else
     ret = keyc2vkey[keycode];
+#endif
 
     if (!keycode || !ret)
     {
@@ -1737,6 +2280,14 @@
     }
 
     index = -1;
+#ifdef XKB_EXP
+    for (i = 0; i < codes[xkb_group][keycode]; i++) {
+      if (keyc2ksym[xkb_group][keycode][i] == keysym) {
+         index = i;
+         break;
+      }
+    }
+#else
     wine_tsx11_lock();
     for (i = 0; i < 4; i++) /* find shift state */
     {
@@ -1747,6 +2298,7 @@
         }
     }
     wine_tsx11_unlock();
+#endif
 
     switch (index)
     {
@@ -1790,8 +2342,13 @@
 			/* let's do vkey -> keycode -> scan */
 			int keyc;
 			for (keyc=min_keycode; keyc<=max_keycode; keyc++)
+#ifdef XKB_EXP
+				if ((keyc2vkey[xkb_group][keyc] & 0xFF) == wCode)
+					returnMVK (keyc2scan[xkb_group][keyc] & 0xFF);
+#else
 				if ((keyc2vkey[keyc] & 0xFF) == wCode)
 					returnMVK (keyc2scan[keyc] & 0xFF);
+#endif
 			TRACE("returning no scan-code.\n");
 		        return 0; }
 
@@ -1799,8 +2356,13 @@
 			/* let's do scan -> keycode -> vkey */
 			int keyc;
 			for (keyc=min_keycode; keyc<=max_keycode; keyc++)
+#ifdef XKB_EXP
+				if ((keyc2scan[xkb_group][keyc] & 0xFF) == (wCode & 0xFF))
+					returnMVK (keyc2vkey[xkb_group][keyc] & 0xFF);
+#else
 				if ((keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
 					returnMVK (keyc2vkey[keyc] & 0xFF);
+#endif
 			TRACE("returning no vkey-code.\n");
 		        return 0; }
 
@@ -1828,13 +2390,28 @@
 			/* We exit on the first keycode found, to speed up the thing. */
 			for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
 			{ /* Find a keycode that could have generated this virtual key */
+#ifdef XKB_EXP
+			    if  ((keyc2vkey[xkb_group][keyc] & 0xFF) == wCode)
+#else
 			    if  ((keyc2vkey[keyc] & 0xFF) == wCode)
+#endif
 			    { /* We filter the extended bit, we don't know it */
-			        e.keycode = keyc; /* Store it temporarily */
-				if ((EVENT_event_to_vkey(0,&e) & 0xFF) != wCode) {
-				    e.keycode = 0; /* Wrong one (ex: because of the NumLock
-					 state), so set it to 0, we'll find another one */
-				}
+#ifdef XKB_EXP
+                                e.keycode = keyc2over[xkb_overlay][keyc]; /* Store it temporarily */
+                                TRACE_(key)("Keycode %02x turned back to %02x\n",
+                                            keyc, e.keycode);
+#else
+                                e.keycode = keyc; /* Store it temporarily */
+#endif
+                                if ((EVENT_event_to_vkey(0,&e) & 0xFF) != wCode) {
+                                    e.keycode = 0; /* Wrong one (ex: because of the NumLock
+                                         state), so set it to 0, we'll find another one */
+                                }
+#ifdef XKB_EXP
+                                else {
+                                    e.keycode = keyc2over[xkb_overlay][keyc]; /* again */
+                                }
+#endif
 			    }
 			}
 
@@ -1947,7 +2524,11 @@
   /* let's do scancode -> keycode -> keysym -> String */
 
   for (keyi=min_keycode; keyi<=max_keycode; keyi++)
+#ifdef XKB_EXP
+      if ((keyc2scan[xkb_group][keyi]) == scanCode)
+#else
       if ((keyc2scan[keyi]) == scanCode)
+#endif
          break;
   if (keyi <= max_keycode)
   {
@@ -2077,7 +2658,8 @@
     XKeyEvent e;
     KeySym keysym = 0;
     INT ret;
-    int keyc;
+    int keyc, savetype;
+    unsigned int savestate;
     char lpChar[10];
     HWND focus;
     XIC xic;
@@ -2139,13 +2721,28 @@
     /* We exit on the first keycode found, to speed up the thing. */
     for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
       { /* Find a keycode that could have generated this virtual key */
+#ifdef XKB_EXP
+          if  ((keyc2vkey[xkb_group][keyc] & 0xFF) == virtKey)
+#else
           if  ((keyc2vkey[keyc] & 0xFF) == virtKey)
+#endif
           { /* We filter the extended bit, we don't know it */
+#ifdef XKB_EXP
+              e.keycode = keyc2over[xkb_overlay][keyc]; /* Store it temporarily */
+              TRACE_(key)("Keycode %02x turned back to %02x\n",
+                           keyc, e.keycode);
+#else
               e.keycode = keyc; /* Store it temporarily */
+#endif
               if ((EVENT_event_to_vkey(xic,&e) & 0xFF) != virtKey) {
                   e.keycode = 0; /* Wrong one (ex: because of the NumLock
                          state), so set it to 0, we'll find another one */
               }
+#ifdef XKB_EXP
+              else {
+                e.keycode = keyc2over[xkb_overlay][keyc]; /* again */
+              }
+#endif
 	  }
       }
 
@@ -2166,10 +2763,16 @@
     TRACE_(key)("type %d, window %lx, state 0x%04x, keycode 0x%04x\n",
 		e.type, e.window, e.state, e.keycode);
 
-    if (xic)
-        ret = XmbLookupString(xic, &e, lpChar, sizeof(lpChar), &keysym, NULL);
+    savestate = e.state;
+    if (xic) {
+          savetype = e.type;
+          if (savetype == KeyRelease) e.type = KeyPress;
+          ret = XmbLookupString(xic, &e, lpChar, sizeof(lpChar), &keysym, NULL);
+          e.type = savetype;
+        }
     else
         ret = XLookupString(&e, lpChar, sizeof(lpChar), &keysym, NULL);
+    e.state = savestate;
     wine_tsx11_unlock();
 
     if (ret == 0)



More information about the wine-patches mailing list