winex11.drv keyboard driver rewrite

Oleh R. Nykyforchyn nick at pu.if.ua
Mon Nov 13 09:15:31 CST 2006


For a rather long time (since wine-2004XXXX) I worked at the most complete
integration of XKB features into Wine. There was an unsuccessful attempt to
submit a large patch to Wine team. I think that it was rejected because
such a big change cannot be made "clean and small" and because of
unwillingness to repair that is not broken.   

I kept working on polishing my implementation and synchronizing it with
the official Wine releases. I also tracked all changes in keyboard driver
and recently I noted that Dmitry Timoshkov introduces things that I already
did in my code. This encourages me to make an another attempt to share my 
extensions (that now go much further) with others.

The most serious problem to me was that Wine can correctly deal with XKB
enabled X11, but only for the simplest configuration : one or two XKB groups,
with two keysyms for a keycode in each. Thus we can choose one of the two
variants:

1) there are two groups, e.g. US/ASCII and Russian, so Wine should contain
a dual (English/Russian) layout. It makes impossible to detect input language
depending on the keyboard state. We also cannot have more than two layouts.
We cannot also combine, e.g., French and Russian, because it takes 3+2>4 keysyms
per keycode.

2) there are several XKB layouts, and each of them contains only one XKB group
(in fact, a layout for one language). This simplifies the thing, but each time
we switch the layout, new keyboard mapping is loaded into X, and Wine keyboard
driver should redetect everything from the beginning. Furthermore, Wine never
can know how much layouts are there on the system, thus calls like
ActivateKeyboardLayout() can't be implemented.
 
As far as XKB extension exists and is widely used on most of Unices, it is wise
to use its features to the maximal extent. In theory, four XKB groups and two
overlay groups allow to have to 16 different layouts and switch between them
just by X means, without need to reload another mapping into X server. Of
course, reality isn't so perfect, but I wrote a simple "Cyrillic-Eastern/Western
European" layout that uses three XKB groups: one for English and German, an
another for English/Polish and the third for Ukrainian/Russian/Belarusian,
i.e., six of possible twelve layouts. If I used the last remaining XKB group,
I could easily have two or three layouts more, for example, Chech, Slovak and
so on can be added.

Here an another X/Unix problem arises. IFAIK, You cannot type chars
that are not supported by Your current locale. It would be nice if X switched
the "input locale", but I don't know anything about it. So I can't see German
umlauts or Belarussian short_u. For Wine this means that I can't make a M$
Office document that contains simultaneously Ukrainian, Belarussian, Polish and
German characters.

I partially rewrote Wine keyboard driver to avoid these restrictions. The most
notable changes are:

1) all layouts for current XKB configuration are detected independently and
listed only once. They are redetected only if X server signals that keyboard
mapping is changed;

2) for each possible combination (XKB group, Overlay1 state, Overlay2 state) two
modes of lookup are tried : determined by current locale and by
native locale for the layout (if not specified, DEFAULTUNIXCODEPAGE from
kernel/nls is used);

3) for each detected LCID the closest candidate of possible 16 layouts is chosen
and used to implement ActivateKeyboardLayout();

4) You can manually configure each of 16 (or 1) layouts through registry,
by "HKCU/Software/Wine/X11 Driver/Keyboard/Layout{0--15}"
and (or) "HKCU/Software/Wine/X11 Driver/Keyboard/Codepage{0--15}";

5) now GetKeyboardLayout() returns the LCID for the current layout, and an
application (e.g., MS Word) is notified about changes.

My implementation worked for me without serious changes since at least
Wine-20041019. Upgrade to new releases is made by a simple script. The only
thing that needed hand work was renaming of x11drv directory to winex11.drv.
I have a snapshot (although don't attach it here because of its large size)
that shows a text typed in Word (MS Office 2000) sequentially in German,
French, Polish, Belarusian, Ukrainian and Russian (locale is KOI8-U and
supports only English, Russian and Ukrainian letters).

I doubt that it is possible in core Wine. I think that other Wine users can
benefit from my work. The problem is that such a patch cannot be kept "small
and clear", as recommended. We cannot jump across an abyss in several small
steps. Another question is that all testing was done only on Linux+X.Org+XKB
(I just have no anything else), although I tried to preserve all functionality
in non-XKB case. The structure of code is such that if at last X11 support
multiple input locales, only minimal changes will be needed. Now I propose my
implementation for discussion and/or testing.

I haven't prepared the patch with git because it is too difficult for me to
pull a 80 Gb repository by my slow dialup connection, especially when I am not
sure that it will be used by anybody. The patch is prepared by "diff -c" against
the last Wine release (0.9.25). If it worth doing, I will ask my friends to pull
the Git repository through a faster channel or (if it is appropriate) turn the
unpacked source into a local Git repository and then make a patch.  Anyway, the
patch is too large to be useful for a person who wants to understand the
changes. Thus I also attach new versions of the changed files.

PS. If You have an ordinary en+... XKB keyboard setup (en+ru, en+ua or so), then
the patched Wine should work out of the box. To use
English/German/Polish/Ukrainian/Russian/Belarussian/... simultaneously, some
little hacking is needed. You are to write a simple XKB configuration  or to take
mine (then You will have also almost all european accents: acute, grave, diaeresis,
tilde, circumflex, cedilla, macron, caron, doubleacute; here are also double angle
brackets, i.e. guillemotsleft and guillemotsright). You may also append to You
locale/Compose file some composition rules for european languages (the simplest
way is to add the contents of iso-8859-15/Compose). Of course, my XKB layout
isn't mature enough to propose it for inclusion into official release, but it
works well for me in Office 2000 (I was also to set "riched20=n,imm32=n" in
winecfg).

To ChangeLog:
   X11DRV keyboard driver partially rewritten to correctly handle
XKB extension features. The most notable changes are:
  1) all layouts for current XKB configuration are detected independently and
listed only once. They are redetected only if X server signals that keyboard
mapping is changed. Thus with XKB enabled You can have (in theory) up to
16 layouts;
  2) for each possible combination (XKB group, Overlay1 state, Overlay2 state)
two modes of lookup are tried : determined by current locale and by
native locale for the layout (if not specified, DEFAULTUNIXCODEPAGE from
kernel/nls is used);
  3) for each detected LCID the closest candidate of possible 16 layouts is
chosen and used to implement ActivateKeyboardLayout();
  4) You can manually configure each of 16 (or 1) layouts through registry,
by "HKCU/Software/Wine/X11 Driver/Keyboard/Layout{0--15}"
and (or) "HKCU/Software/Wine/X11 Driver/Keyboard/Codepage{0--15}";
  5) now GetKeyboardLayout() returns the LCID for the current layout, and an
application (e.g., MS Word) is notified about changes.

--- wine-0.9/dlls/winex11.drv/x11drv_main.c.orig	2006-11-13 12:04:22.000000000 +0200
+++ wine-0.9/dlls/winex11.drv/x11drv_main.c	2006-11-13 12:04:09.000000000 +0200
@@ -75,6 +75,7 @@
 int usexvidmode = 1;
 int usexrandr = 1;
 int use_xkb = 1;
+int xkbEventCode = 0;
 int use_take_focus = 1;
 int use_primary_selection = 0;
 int managed_mode = 1;
@@ -519,24 +520,23 @@
 #ifdef HAVE_XKB
     if (use_xkb)
     {
-        use_xkb = XkbUseExtension( data->display, NULL, NULL );
-        if (use_xkb)
-        {
-            /* Hack: dummy call to XkbKeysymToModifiers to force initialisation of Xkb internals */
-            /* This works around an Xlib bug where it tries to get the display lock */
-            /* twice during XFilterEvents if Xkb hasn't been initialised yet. */
-            XkbKeysymToModifiers( data->display, 'A' );
-            XkbSetDetectableAutoRepeat( data->display, True, NULL );
-        }
+        use_xkb = XkbQueryExtension( data->display, 
+	                             NULL, &xkbEventCode, NULL , NULL , NULL );
+        if (use_xkb) {
+	  XkbSetDetectableAutoRepeat( data->display, True, NULL );
+	  XkbSelectEvents(data->display, XkbUseCoreKbd,
+	     XkbAllEventsMask,
+	     XkbNewKeyboardNotifyMask | XkbMapNotifyMask | 
+	     XkbStateNotifyMask | XkbControlsNotifyMask);
+	  X11DRV_register_event_handler(xkbEventCode, X11DRV_XkbEvent);
+	}
     }
 #endif
 
     if (TRACE_ON(synchronous)) XSynchronize( data->display, True );
     wine_tsx11_unlock();
 
-    if (!use_xim)
-        data->xim = NULL;
-    else if (!(data->xim = X11DRV_SetupXIM( data->display, input_style )))
+    if (use_xim && !(data->xim = X11DRV_SetupXIM( data->display, input_style )))
         WARN("Input Method is not available\n");
 
     if (wine_server_fd_to_handle( ConnectionNumber(data->display), GENERIC_READ | SYNCHRONIZE,
--- wine-0.9/dlls/winex11.drv/x11drv.h.orig	2006-11-13 12:04:23.000000000 +0200
+++ wine-0.9/dlls/winex11.drv/x11drv.h	2006-11-13 12:04:22.000000000 +0200
@@ -622,6 +622,9 @@
 extern void X11DRV_SelectionRequest( HWND hWnd, XEvent *event );
 extern void X11DRV_SelectionClear( HWND hWnd, XEvent *event );
 extern void X11DRV_MappingNotify( HWND hWnd, XEvent *event );
+#ifdef HAVE_XKB
+extern void X11DRV_XkbEvent( HWND hwnd, XEvent *event );
+#endif
 
 extern DWORD EVENT_x11_time_to_win32_time(Time time);
 
--- wine-0.9/dlls/winex11.drv/event.c.orig	2006-11-13 12:04:23.000000000 +0200
+++ wine-0.9/dlls/winex11.drv/event.c	2006-11-13 12:04:09.000000000 +0200
@@ -26,6 +26,9 @@
 #include <X11/Xlib.h>
 #include <X11/Xresource.h>
 #include <X11/Xutil.h>
+#ifdef HAVE_XKB
+#include <X11/XKBlib.h>
+#endif
 
 #include <assert.h>
 #include <stdarg.h>
@@ -238,31 +241,36 @@
  */
 static int process_events( Display *display, ULONG_PTR mask )
 {
-    XEvent event;
+    union _event {
+      XEvent core;
+#ifdef HAVE_XKB
+      XkbEvent space;
+#endif
+    } event;
     HWND hwnd;
     int count = 0;
     x11drv_event_handler handler;
 
     wine_tsx11_lock();
-    while (XCheckIfEvent( display, &event, filter_event, (char *)mask ))
+    while (XCheckIfEvent( display, &event.core, filter_event, (char *)mask ))
     {
         count++;
-        if (XFilterEvent( &event, None )) continue;  /* filtered, ignore it */
+        if (XFilterEvent( &event.core, None )) continue;  /* filtered, ignore it */
 
-        if (!(handler = find_handler( event.type )))
+        if (!(handler = find_handler( event.core.type )))
         {
-            TRACE( "%s, ignoring\n", dbgstr_event( event.type ));
+            TRACE( "%s, ignoring\n", dbgstr_event( event.core.type ));
             continue;  /* no handler, ignore it */
         }
 
-        if (XFindContext( display, event.xany.window, winContext, (char **)&hwnd ) != 0)
+        if (XFindContext( display, event.core.xany.window, winContext, (char **)&hwnd ) != 0)
             hwnd = 0;  /* not for a registered window */
-        if (!hwnd && event.xany.window == root_window) hwnd = GetDesktopWindow();
+        if (!hwnd && event.core.xany.window == root_window) hwnd = GetDesktopWindow();
 
         wine_tsx11_unlock();
         TRACE( "%s for hwnd/window %p/%lx\n",
-               dbgstr_event( event.type ), hwnd, event.xany.window );
-        handler( hwnd, &event );
+               dbgstr_event( event.core.type ), hwnd, event.core.xany.window );
+        handler( hwnd, &event.core );
         wine_tsx11_lock();
     }
     XFlush( gdi_display );
--- wine-0.9/dlls/winex11.drv/keyboard.c.orig	2006-11-13 12:04:23.000000000 +0200
+++ wine-0.9/dlls/winex11.drv/keyboard.c	2006-11-13 13:45:05.000000000 +0200
@@ -7,6 +7,7 @@
  * Copyright 1998 Morten Welinder
  * Copyright 1998 Ulrich Weigand
  * Copyright 1999 Ove KЕven
+ * Copyright 2005 Oleh Nykyforchyn
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -30,13 +31,21 @@
 #include <X11/Xlib.h>
 #include <X11/Xresource.h>
 #include <X11/Xutil.h>
-#ifdef HAVE_X11_XKBLIB_H
+#ifdef HAVE_XKB
 #include <X11/XKBlib.h>
+#define MAX_OVERLAYS 4
+#define OVER1_ENABLED 1
+#define OVER2_ENABLED 2
+#define MAX_LAYOUTS 16
+#else
+#define MAX_OVERLAYS 1
+#define MAX_LAYOUTS 1
 #endif
 
 #include <ctype.h>
 #include <stdarg.h>
 #include <string.h>
+#include <stdio.h>
 
 #define NONAMELESSUNION
 #define NONAMELESSSTRUCT
@@ -46,14 +55,16 @@
 #include "winuser.h"
 #include "wine/winuser16.h"
 #include "winnls.h"
+#include "winreg.h"
 #include "win.h"
 #include "x11drv.h"
 #include "wine/server.h"
 #include "wine/unicode.h"
 #include "wine/debug.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
-WINE_DECLARE_DEBUG_CHANNEL(key);
+WINE_DEFAULT_DEBUG_CHANNEL(keyboard); /* Managing the whole layouts set */
+WINE_DECLARE_DEBUG_CHANNEL(layout);   /* How each layout is detected */
+WINE_DECLARE_DEBUG_CHANNEL(key);      /* Processing single key event */
 
 typedef union
 {
@@ -86,8 +97,31 @@
 static BYTE TrackSysKey = 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
                                 or a WM_KEYUP message */
 
-static int min_keycode, max_keycode, keysyms_per_keycode;
-static WORD keyc2vkey[256], keyc2scan[256];
+static int min_keycode = 0, max_keycode = 0, keysyms_per_keycode = 4;
+static int layout_no = 0; /* Current layout */
+static int max_group = 1; /* Can be 1 to 4 XKB groups; ignored if XKB is not used */
+#ifdef HAVE_XKB
+static int kbd_group = 0;   /* Current XKB group */
+static int kbd_overlay = 0; /* Active overlay groups : 0 = none,
+         1=overlay1, 2=overlay2, 3=both */
+static KeyCode keyc2over[MAX_OVERLAYS-1][256] = {{0}}; /* real key mapped to virtual */
+static KeyCode over2keyc[MAX_OVERLAYS-1][256] = {{0}}; /* the least real key mapped 
+                                                      by overlay to this virtual one */
+#endif
+static WORD keyc2vkey[MAX_LAYOUTS][256] = {{0}}, \
+            keyc2scan[MAX_LAYOUTS][256] = {{0}};
+static KeySym keyc2ksym[MAX_LAYOUTS][256][4] = {{{0}}};
+static unsigned kbd_layout[MAX_LAYOUTS]= {0}; /* indices into table of 
+                                         layouts for possible combinations 
+                                         of XKB group and overlay group */
+static int kbd_list[MAX_LAYOUTS];       /* scores for preferred layouts */
+static unsigned kbd_cp[MAX_LAYOUTS]; /* Win number of the Unix codepage for a layout, 0 = locale encoding */
+static unsigned global_refresh = 1;     /* rebuild all tables */
+#ifdef HAVE_XKB
+XkbDescPtr desc = NULL; /* XKB keyboard description record - here we
+                           quickly get all XKB information */
+#endif
+static const char * INIKeyboard = "Software\\Wine\\X11 Driver\\Keyboard";
 
 static int NumLockMask, AltGrMask; /* mask in the XKeyEvent state */
 static int kcControl, kcAlt, kcShift, kcNumLock, kcCapsLock; /* keycodes */
@@ -240,7 +274,7 @@
    and Shift-AltGr if it can vary among different X servers */
 /* Remember that your 102nd key (to the right of l-shift) should be on a
    separate line, see existing tables */
-/* If Wine fails to match your new table, use WINEDEBUG=+key to find out why */
+/* If Wine fails to match your new table, use WINEDEBUG=+layout to find out why */
 /* Remember to also add your new table to the layout index table far below! */
 
 /*** German Logitech Desktop Pro keyboard layout */
@@ -341,6 +375,16 @@
  "<>|","yY","xX","cC","vV","bB","nN","mM",",;",".:","-_",
 };
 
+/*** German keyboard layout (simple, minimal difference from US layout) */
+static const char main_key_DE_std[MAIN_LEN][4] =
+{
+ "\xdf\xb0","1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+",
+ "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","Ээ","`'~",
+ "aA","sS","dD","fF","gG","hH","jJ","kK","lL","Жж","Дд","\\|",
+ "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",
+ "<>" /* the phantom key */
+};
+
 /*** Swiss German keyboard layout (setxkbmap ch -variant de) */
 static const char main_key_SG[MAIN_LEN][4] =
 {
@@ -572,6 +616,16 @@
  "<>" /* the phantom key */
 };
 
+/*** Belarusian keyboard layout KOI8-RU */
+static const char main_key_BY_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] =
 {
@@ -612,6 +666,16 @@
  "<>|"
 };
 
+/*** Polish keyboard layout (simple, minimal difference from US layout) */
+static const char main_key_PL_std[MAIN_LEN][4] =
+{
+ "`~","1!","2@","3#","4$","5%","6^","7&","8*","9(","0)","-_","=+",
+ "qQ","wW","eE","rR","tT","yY","uU","iI","oO","pP","\xea\xca","\xb3\xa3",
+ "aA","sS","dD","fF","gG","hH","jJ","kK","lL","\xf3\xd3","\xb1\xa1","\\|",
+ "zZ","xX","cC","vV","bB","nN","mM",",<",".>","/?",
+ "<>" /* the phantom key */
+};
+
 /*** Slovenian keyboard layout (setxkbmap si) ***/
 static const char main_key_SI[MAIN_LEN][4] =
 {
@@ -895,82 +959,91 @@
 
 
 /*** Layout table. Add your keyboard mappings to this list */
-static const struct {
-    LCID lcid; /* input locale identifier, look for LOCALE_ILANGUAGE
+static struct {
+    const LCID lcid; /* input locale identifier, look for LOCALE_ILANGUAGE
                  in the appropriate dlls/kernel/nls/.nls file */
-    const char *comment;
-    const char (*key)[MAIN_LEN][4];
-    const WORD (*scan)[MAIN_LEN]; /* scan codes mapping */
-    const WORD (*vkey)[MAIN_LEN]; /* virtual key codes mapping */
+    unsigned int codepage; /* If keysyms in a layout are outside of Your locale,
+		but You want to input them, and XKB is enabled, then Wine will
+		try to simply cut out lower byte of each keysym (raw lookup)
+		instead of converting to current locale (locale lookup).
+		If chars produced this way match some Windows codepage, write
+		it here.
+		  0 = DefaultUnixCodepage for lcid (fallback)  */
+    const char * const comment;
+    const char (* const key)[MAIN_LEN][4];
+    const WORD (* const scan)[MAIN_LEN]; /* scan codes mapping */
+    const WORD (* const vkey)[MAIN_LEN]; /* virtual key codes mapping */
 } main_key_tab[]={
- {0x0409, "United States keyboard layout", &main_key_US, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_scan_dvorak, &main_key_vkey_dvorak},
- {0x0409, "United States International keyboard layout", &main_key_US_intl, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0809, "British keyboard layout", &main_key_UK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0407, "German keyboard layout", &main_key_DE, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0407, "German keyboard layout without dead keys", &main_key_DE_nodead, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0407, "German keyboard layout for logitech desktop pro", &main_key_DE_logitech,  &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0407, "German keyboard layout without dead keys 105", &main_key_DE_nodead_105, &main_key_scan_qwerty, &main_key_vkey_qwertz_105},
- {0x0807, "Swiss German keyboard layout", &main_key_SG, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x100c, "Swiss French keyboard layout", &main_key_SF, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x041d, "Swedish keyboard layout", &main_key_SE, &main_key_scan_qwerty, &main_key_vkey_qwerty_v2},
- {0x0425, "Estonian keyboard layout", &main_key_ET, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0414, "Norwegian keyboard layout", &main_key_NO, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0406, "Danish keyboard layout", &main_key_DA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040c, "French keyboard layout", &main_key_FR, &main_key_scan_qwerty, &main_key_vkey_azerty},
- {0x0c0c, "Canadian French keyboard layout", &main_key_CF, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0c0c, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0c0c, "Canadian keyboard layout", &main_key_CA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x080c, "Belgian keyboard layout", &main_key_BE, &main_key_scan_qwerty, &main_key_vkey_azerty},
- {0x0816, "Portuguese keyboard layout", &main_key_PT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0416, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br, &main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty},
- {0x0416, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr,&main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty},
- {0x040b, "Finnish keyboard layout", &main_key_FI, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0402, "Bulgarian bds keyboard layout", &main_key_BG_bds, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0402, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0423, "Belarusian keyboard layout", &main_key_BY, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout", &main_key_RU, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {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},
- {0x040e, "Hungarian keyboard layout", &main_key_HU, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0415, "Polish (programmer's) keyboard layout", &main_key_PL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0424, "Slovenian keyboard layout", &main_key_SI, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0c1a, "Serbian keyboard layout sr", &main_key_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
- {0x0c1a, "Serbian keyboard layout us,sr", &main_key_US_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
- {0x041a, "Croatian keyboard layout", &main_key_HR, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x041a, "Croatian keyboard layout (specific)", &main_key_HR_jelly, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0411, "Japanese 106 keyboard layout", &main_key_JA_jp106, &main_key_scan_qwerty_jp106, &main_key_vkey_qwerty_jp106},
- {0x0411, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041b, "Slovak keyboard layout", &main_key_SK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041b, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0405, "Czech keyboard layout", &main_key_CS, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0405, "Czech keyboard layout cz", &main_key_CZ, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0405, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040a, "Latin American keyboard layout", &main_key_LA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0427, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041f, "Turkish keyboard layout", &main_key_TK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041f, "Turkish keyboard layout tr", &main_key_TR, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041f, "Turkish keyboard layout trf", &main_key_TR_F, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040d, "Israelian keyboard layout", &main_key_IL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040d, "Israelian phonetic keyboard layout", &main_key_IL_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040d, "Israelian Saharon keyboard layout", &main_key_IL_saharon, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0409, "VNC keyboard layout", &main_key_vnc, &main_key_scan_vnc, &main_key_vkey_vnc},
- {0x0408, "Greek keyboard layout", &main_key_EL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041e, "Thai (Kedmanee)  keyboard layout", &main_key_th, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0413, "Dutch keyboard layout", &main_key_NL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0409,     0, "United States keyboard layout", &main_key_US, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0409,     0, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0409,     0, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_scan_dvorak, &main_key_vkey_dvorak},
+ {0x0409,     0, "United States International keyboard layout", &main_key_US_intl, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0809,     0, "British keyboard layout", &main_key_UK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0407,     0, "German keyboard layout", &main_key_DE, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x0407,     0, "German keyboard layout without dead keys", &main_key_DE_nodead, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x0407,     0, "German keyboard layout for logitech desktop pro", &main_key_DE_logitech,  &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x0407,     0, "German keyboard layout without dead keys 105", &main_key_DE_nodead_105, &main_key_scan_qwerty, &main_key_vkey_qwertz_105},
+ {0x0407,     0, "German keyboard layout (simple)", &main_key_DE_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0807,     0, "Swiss German keyboard layout", &main_key_SG, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x100c,     0, "Swiss French keyboard layout", &main_key_SF, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x041d,     0, "Swedish keyboard layout", &main_key_SE, &main_key_scan_qwerty, &main_key_vkey_qwerty_v2},
+ {0x0425,     0, "Estonian keyboard layout", &main_key_ET, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0414,     0, "Norwegian keyboard layout", &main_key_NO, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0406,     0, "Danish keyboard layout", &main_key_DA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040c,     0, "French keyboard layout", &main_key_FR, &main_key_scan_qwerty, &main_key_vkey_azerty},
+ {0x0c0c,     0, "Canadian French keyboard layout", &main_key_CF, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0c0c,     0, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0c0c,     0, "Canadian keyboard layout", &main_key_CA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x080c,     0, "Belgian keyboard layout", &main_key_BE, &main_key_scan_qwerty, &main_key_vkey_azerty},
+ {0x0816,     0, "Portuguese keyboard layout", &main_key_PT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0416,     0, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br, &main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty},
+ {0x0416,     0, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr,&main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty},
+ {0x040b,     0, "Finnish keyboard layout", &main_key_FI, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0402,     0, "Bulgarian bds keyboard layout", &main_key_BG_bds, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0402,     0, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0423, 21866, "Belarusian keyboard layout", &main_key_BY, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0419, 20866, "Russian keyboard layout", &main_key_RU, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0419, 20866, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0419, 20866, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0419,  1251, "Russian keyboard layout cp1251", &main_key_RU_cp1251, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0419, 20866, "Russian phonetic keyboard layout", &main_key_RU_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0422, 21866, "Ukrainian keyboard layout KOI8-U", &main_key_UA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0422, 21866, "Ukrainian keyboard layout (standard)", &main_key_UA_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0419, 20866, "Russian keyboard layout (standard)", &main_key_RU_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0423, 21866, "Belarusian keyboard layout (standard)", &main_key_BY_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040a,     0, "Spanish keyboard layout", &main_key_ES, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0410,     0, "Italian keyboard layout", &main_key_IT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040f,     0, "Icelandic keyboard layout", &main_key_IS, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040e,     0, "Hungarian keyboard layout", &main_key_HU, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x0415,     0, "Polish (programmer's) keyboard layout", &main_key_PL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0415,     0, "Polish keyboard layout (simple)", &main_key_PL_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0424,     0, "Slovenian keyboard layout", &main_key_SI, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x0c1a,     0, "Serbian keyboard layout sr", &main_key_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
+ {0x0c1a,     0, "Serbian keyboard layout us,sr", &main_key_US_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
+ {0x041a,     0, "Croatian keyboard layout", &main_key_HR, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x041a,     0, "Croatian keyboard layout (specific)", &main_key_HR_jelly, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0411,     0, "Japanese 106 keyboard layout", &main_key_JA_jp106, &main_key_scan_qwerty_jp106, &main_key_vkey_qwerty_jp106},
+ {0x0411,     0, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x041b,     0, "Slovak keyboard layout", &main_key_SK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x041b,     0, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0405,     0, "Czech keyboard layout", &main_key_CS, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0405,     0, "Czech keyboard layout cz", &main_key_CZ, &main_key_scan_qwerty, &main_key_vkey_qwertz},
+ {0x0405,     0, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040a,     0, "Latin American keyboard layout", &main_key_LA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0427,     0, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x041f,     0, "Turkish keyboard layout", &main_key_TK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x041f,     0, "Turkish keyboard layout tr", &main_key_TR, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x041f,     0, "Turkish keyboard layout trf", &main_key_TR_F, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040d,     0, "Israelian keyboard layout", &main_key_IL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040d,     0, "Israelian phonetic keyboard layout", &main_key_IL_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x040d,     0, "Israelian Saharon keyboard layout", &main_key_IL_saharon, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0409,     0, "VNC keyboard layout", &main_key_vnc, &main_key_scan_vnc, &main_key_vkey_vnc},
+ {0x0408,     0, "Greek keyboard layout", &main_key_EL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x041e,     0, "Thai (Kedmanee)  keyboard layout", &main_key_th, &main_key_scan_qwerty, &main_key_vkey_qwerty},
+ {0x0413,     0, "Dutch keyboard layout", &main_key_NL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
 
- {0, NULL, NULL, NULL, NULL} /* sentinel */
+ {0, 0, NULL, NULL, NULL, NULL} /* sentinel */
 };
-static unsigned kbd_layout=0; /* index into above table of layouts */
 
 /* maybe more of these scancodes should be extended? */
                 /* extended must be set for ALT_R, CTRL_R,
@@ -1076,32 +1149,198 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
 };
 
+/* Forward declarations */
+
+static void
+KEYBOARD_DetectLayouts(void);
+
+void
+X11DRV_InitKeyboard(void);
+
+inline static KeyCode
+OverToKeyc(KeyCode k);
+
+HKL X11DRV_GetKeyboardLayout(DWORD);
+
+
+/**********************************************************************
+ *		KEYBOARD_LayoutSwitched
+ *
+ *     Track changes of effective XKB group and overlay group
+ */
+
+static Bool
+KEYBOARD_LayoutSwitched (void)
+{ /* enter and exit in tsx11_lock'ed state */
+  int changed = 0;
+  Display *display = thread_display();
+#ifdef HAVE_XKB
+  if (use_xkb) {
+    unsigned ctrls;
+    XkbStateRec state;
+    XkbGetState(display, XkbUseCoreKbd, &state);
+    keysyms_per_keycode = 4; /* ??? */
+    kbd_group = state.locked_group;
+    desc = XkbGetKeyboard(display,
+           XkbAllComponentsMask,
+           XkbUseCoreKbd);
+    XkbGetControls(display, XkbAllControlsMask, desc);
+    ctrls = desc->ctrls->enabled_ctrls;
+    kbd_overlay = 0;
+    if (ctrls & XkbOverlay1Mask) kbd_overlay  = OVER1_ENABLED;
+    if (ctrls & XkbOverlay2Mask) kbd_overlay |= OVER2_ENABLED;
+    changed = MAX_OVERLAYS*kbd_group + kbd_overlay;
+    if (layout_no != changed) {
+        layout_no = changed;
+	changed = 1;
+    } else changed = 0;
+    if (desc->min_key_code != min_keycode) {
+	TRACE(": min_key_code changed from %d to %d\n",
+          min_keycode, desc->min_key_code); 
+        min_keycode = desc->min_key_code;
+	changed = global_refresh = 1;
+    }
+    if (desc->max_key_code != max_keycode) {
+	TRACE(": max_key_code changed from %d to %d\n",
+          max_keycode, desc->max_key_code); 
+        max_keycode = desc->max_key_code;
+	changed = global_refresh = 1;
+    }
+    if (desc->ctrls->num_groups != max_group) {
+	TRACE(": num_groups changed from %d to %d\n",
+          max_group, desc->ctrls->num_groups); 
+        max_group = desc->ctrls->num_groups;
+	changed = global_refresh = 1;
+    }
+    if (global_refresh)
+      TRACE("caused global refresh: %d groups, keycodes from %d to %d\n",
+          max_group, min_keycode, max_keycode); 
+    else
+      TRACE("Selected XKB group %d, overlay %d\n",
+          kbd_group, kbd_overlay);
+  } else
+#endif /* HAVE_XKB */
+  {
+    int min_key_code, max_key_code, have_chars;
+    layout_no = 0;
+    changed   = 0;
+    XDisplayKeycodes(display, &min_key_code, &max_key_code);
+    XFree(XGetKeyboardMapping(display,
+      min_key_code, max_key_code - min_key_code +1, &have_chars));
+    if ((min_key_code != min_keycode) || (max_key_code != max_keycode) ||
+        (have_chars != keysyms_per_keycode)) {
+	min_keycode = min_key_code;
+	max_keycode = max_key_code;
+        keysyms_per_keycode = have_chars;
+	changed = global_refresh = 1;
+        TRACE("caused global refresh: max %d keysyms, keycodes from %d to %d\n",
+          have_chars, min_keycode, max_keycode);
+    }
+  }
+#ifdef HAVE_XKB
+  if (use_xkb && desc) {
+    XkbFreeKeyboard(desc, XkbAllComponentsMask,True);
+    desc = NULL;
+  }
+  if (changed) {
+    if (global_refresh) 
+     TRACE("to layout #%d - detect it later\n",layout_no);
+    else {
+     HWND hwnd = GetFocus();
+     if (!hwnd) hwnd = GetActiveWindow();
+     PostMessageW(hwnd, WM_INPUTLANGCHANGEREQUEST,
+               0 /*FIXME*/, (LPARAM)X11DRV_GetKeyboardLayout(0));
+     TRACE("to layout #%d, \"%s\", codepage %d\n",
+         layout_no, main_key_tab[kbd_layout[layout_no]].comment,kbd_cp[layout_no]);
+    }
+  }
+#endif
+  return changed;
+}
+
+/**********************************************************************
+ *              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)
 {
+    KeyCode kc = 0;
     KeySym keysym = 0;
     Status status;
     char buf[24];
-
-    if (xic)
-        XmbLookupString(xic, e, buf, sizeof(buf), &keysym, &status);
+    WORD vkey = 0;
+    int savetype, check, i;
+    unsigned int savestate;
+
+    TRACE_(key)("Received e->keycode = %x\n", e->keycode);
+
+    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, buf, sizeof(buf), &keysym, &status);
+          e->type = savetype;
+        }
     else
         XLookupString(e, buf, sizeof(buf), &keysym, NULL);
+    e->state = savestate;
+
+
+#ifdef HAVE_XKB
+    if (use_xkb) check = 2;
+    else
+#endif
+    check = 1;
+
+  again:
+    kc = OverToKeyc(e->keycode);
+    if (kc != e->keycode)
+       TRACE_(key)("Treated as e->keycode = %x\n", kc);
 
     if ((e->state & NumLockMask) &&
         (keysym == XK_KP_Separator || keysym == XK_KP_Decimal ||
          (keysym >= XK_KP_0 && keysym <= XK_KP_9)))
         /* Only the Keypad keys 0-9 and . send different keysyms
          * depending on the NumLock state */
-        return nonchar_key_vkey[keysym & 0xFF];
+        vkey = nonchar_key_vkey[keysym & 0xFF];
+    else
+        vkey = keyc2vkey[layout_no][kc];
 
-    TRACE_(key)("e->keycode = %x\n", e->keycode);
+    for (i = 0; i < 4; i++) {
+#ifdef HAVE_XKB
+       if (use_xkb) {
+         TRACE_(key)("Compare: 0x%04lx <> 0x%04lx, group %d, overlay %d, level %d\n",
+             keysym,keyc2ksym[layout_no][kc][i], kbd_group, kbd_overlay, i);
+       } else
+#endif
+         TRACE_(key)("Compare: 0x%04lx <> 0x%04lx, level %d\n",
+             keysym,keyc2ksym[layout_no][kc][i], i);
+       if (keyc2ksym[layout_no][kc][i] && (keysym == keyc2ksym[layout_no][kc][i])) {
+            if (vkey) check = 0;
+            break;
+       }
+    }
 
-    return keyc2vkey[e->keycode];
+    switch (check--) {
+         case 2:
+           KEYBOARD_LayoutSwitched();
+           if (global_refresh) KEYBOARD_DetectLayouts();
+	   goto again;
+	 case 1:
+         TRACE_(key)("Matching keycode and level not found for keysym 0x%04lx in layout #%d\n",
+             keysym,layout_no);
+    }
+
+    e->keycode = kc;
+    return vkey;
 }
 
+
+
 static BOOL NumState=FALSE, CapsState=FALSE;
 
 
@@ -1218,7 +1457,7 @@
        don't treat it. It's from the same key press. Then the state goes to ON.
        And from there, a 'release' event will switch off the toggle key. */
     *State=FALSE;
-    TRACE("INTERM : don't treat release of toggle key. key_state_table[%#x] = %#x\n",
+    TRACE_(key)("INTERM : don't treat release of toggle key. key_state_table[%#x] = %#x\n",
           vkey,key_state_table[vkey]);
   } else
     {
@@ -1228,7 +1467,7 @@
 	  {
 	    if (Evtype!=KeyPress)
 	      {
-		TRACE("ON + KeyRelease => generating DOWN and UP messages.\n");
+		TRACE_(key)("ON + KeyRelease => generating DOWN and UP messages.\n");
 	        X11DRV_send_keyboard_input( vkey, scan, down, event_time, 0, 0 );
 	        X11DRV_send_keyboard_input( vkey, scan, up, event_time, 0, 0 );
 		*State=FALSE;
@@ -1238,7 +1477,7 @@
 	else /* it was OFF */
 	  if (Evtype==KeyPress)
 	    {
-	      TRACE("OFF + Keypress => generating DOWN and UP messages.\n");
+	      TRACE_(key)("OFF + Keypress => generating DOWN and UP messages.\n");
 	      X11DRV_send_keyboard_input( vkey, scan, down, event_time, 0, 0 );
 	      X11DRV_send_keyboard_input( vkey, scan, up, event_time, 0, 0 );
 	      *State=TRUE; /* Goes to intermediary state before going to ON */
@@ -1292,7 +1531,7 @@
         for (j = 0; j < 8; j++)
         {
             if (!(event->xkeymap.key_vector[i] & (1<<j))) continue;
-            switch(keyc2vkey[(i * 8) + j] & 0xff)
+            switch(keyc2vkey[layout_no][(i * 8) + j] & 0xff)
             {
             case VK_MENU:    alt = 1; break;
             case VK_CONTROL: control = 1; break;
@@ -1308,7 +1547,7 @@
 /***********************************************************************
  *           X11DRV_KeyEvent
  *
- * Handle a X key event
+ *         Handle a X key event
  */
 void X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
 {
@@ -1317,7 +1556,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_x11_time_to_win32_time(event->time);
     Status status = 0;
@@ -1326,18 +1566,28 @@
 		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, &status);
+          event->type = savetype;
+        }
     else
         ascii_chars = XLookupString(event, Str, sizeof(Str), &keysym, NULL);
+    event->state = savestate;
+
     wine_tsx11_unlock();
 
     /* Ignore some unwanted events */
     if ((keysym >= XK_ISO_Lock && keysym <= XK_ISO_Last_Group_Lock) ||
+         keysym == XK_Overlay1_Enable || keysym == XK_Overlay2_Enable ||
          keysym == XK_Mode_switch)
     {
         wine_tsx11_lock();
-        TRACE("Ignoring %s keyboard event\n", XKeysymToString(keysym));
+        TRACE_(key)("Ignoring %s keyboard event\n", XKeysymToString(keysym));
         wine_tsx11_unlock();
         return;
     }
@@ -1396,22 +1646,22 @@
       KEYBOARD_GenerateMsg( VK_NUMLOCK, 0x45, event->type, event_time );
       break;
     case VK_CAPITAL:
-      TRACE("Caps Lock event. (type %d). State before : %#.2x\n",event->type,key_state_table[vkey]);
+      TRACE_(key)("Caps Lock event. (type %d). State before : %#.2x\n",event->type,key_state_table[vkey]);
       KEYBOARD_GenerateMsg( VK_CAPITAL, 0x3A, event->type, event_time );
-      TRACE("State after : %#.2x\n",key_state_table[vkey]);
+      TRACE_(key)("State after : %#.2x\n",key_state_table[vkey]);
       break;
     default:
         /* Adjust the NUMLOCK state if it has been changed outside wine */
 	if (!(key_state_table[VK_NUMLOCK] & 0x01) != !(event->state & NumLockMask))
 	  {
-	    TRACE("Adjusting NumLock state.\n");
+	    TRACE_(key)("Adjusting NumLock state.\n");
 	    KEYBOARD_GenerateMsg( VK_NUMLOCK, 0x45, KeyPress, event_time );
 	    KEYBOARD_GenerateMsg( VK_NUMLOCK, 0x45, KeyRelease, event_time );
 	  }
         /* Adjust the CAPSLOCK state if it has been changed outside wine */
 	if (!(key_state_table[VK_CAPITAL] & 0x01) != !(event->state & LockMask))
 	  {
-              TRACE("Adjusting Caps Lock state.\n");
+            TRACE_(key)("Adjusting Caps Lock state.\n");
 	    KEYBOARD_GenerateMsg( VK_CAPITAL, 0x3A, KeyPress, event_time );
 	    KEYBOARD_GenerateMsg( VK_CAPITAL, 0x3A, KeyRelease, event_time );
 	  }
@@ -1419,7 +1669,7 @@
 	NumState = FALSE;
 	CapsState = FALSE;
 
-	bScan = keyc2scan[event->keycode] & 0xFF;
+	bScan = keyc2scan[layout_no][event->keycode] & 0xFF;
 	TRACE_(key)("bScan = 0x%02x.\n", bScan);
 
 	dwFlags = 0;
@@ -1431,149 +1681,328 @@
    }
 }
 
+#ifdef HAVE_XKB
+
+static const char * const xkb_event_names[] =
+{
+  "XkbNewKeyboardNotify", "XkbMapNotify", "XkbStateNotify",
+  "XkbControlsNotify", "XkbIndicatorStateNotify", "XkbIndicatorMapNotify",
+  "XkbNamesNotify", "XkbCompatMapNotify", "XkbBellNotify", "XkbActionMessage",
+  "XkbAccessXnotify", "XkbExtensionDeviceNotify"
+};
+
+/***********************************************************************
+ *           X11DRV_XkbEvent
+ *
+ *         Handle an XKB event
+ */
+void X11DRV_XkbEvent( HWND hwnd, XEvent * event )
+{
+    XkbEvent* xkbevent = (XkbEvent*) event;
+    TRACE_(key)(" : type %s\n",xkb_event_names[xkbevent->any.xkb_type]);
+    switch ( xkbevent->any.xkb_type ) {
+      case XkbMapNotify:
+        global_refresh = 1;
+      case XkbControlsNotify:
+      case XkbStateNotify:
+        if ( xkbevent->any.xkb_type == XkbStateNotify ){
+          TRACE_(key)(" : changed %x\n", xkbevent->state.changed);
+	  if ( !(xkbevent->state.changed & 
+	     ( XkbGroupStateMask | XkbGroupBaseMask |
+	       XkbGroupLatchMask | XkbGroupLockMask ))) {
+	      TRACE_(key)(" : ignore XkbStateNotify events other than group changes\n"); 
+	      break; 
+	  }
+	}
+	wine_tsx11_lock();
+        KEYBOARD_LayoutSwitched();
+	if (global_refresh) KEYBOARD_DetectLayouts();
+	wine_tsx11_unlock();
+	break;
+      case XkbNewKeyboardNotify:
+        X11DRV_InitKeyboard(); break;
+      default: 
+        break;
+    }
+    
+}
+
 /**********************************************************************
- *		X11DRV_KEYBOARD_DetectLayout
+ *		KEYBOARD_RefreshOverlays
  *
- * Called from X11DRV_InitKeyboard
- *  This routine walks through the defined keyboard layouts and selects
- *  whichever matches most closely.
- * X11 lock must be held.
+ *     Build the tables of conversions done by overlays that
+ *     map some keys to other, so that the same key can behave
+ *     differently in the same XKB group depending on active
+ *     overlay group (e.g. Ukrainian "i" becomes Russian "yeru").
+ *     This way You can join close enough layouts (e.g. Czech
+ *     and Slovak) into single group. Wine will detect them all.
  */
+
 static void
-X11DRV_KEYBOARD_DetectLayout (void)
-{
-  Display *display = thread_display();
-  unsigned current, match, mismatch, seq, i, syms;
-  int score, keyc, key, pkey, ok;
-  KeySym keysym = 0;
-  const char (*lkey)[MAIN_LEN][4];
-  unsigned max_seq = 0;
-  int max_score = 0, ismatch = 0;
-  char ckey[256][4];
-
-  syms = keysyms_per_keycode;
-  if (syms > 4) {
-    WARN("%d keysyms per keycode not supported, set to 4\n", syms);
-    syms = 4;
+KEYBOARD_RefreshOverlays(void) { /* desc should contain server fields */
+  KeyCode keyc, over;
+  int i;  
+  char betype, bedata;
+
+  for (i = 1; i < MAX_OVERLAYS; i++) {
+    for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++) {
+        over = keyc;
+    haveoverlay:
+        betype = (desc->server->behaviors)[over].type;
+        bedata = (desc->server->behaviors)[over].data;
+        if (((betype == XkbKB_Overlay1) && (i & OVER1_ENABLED)) ||
+	    ((betype == XkbKB_Overlay2) && (i & OVER2_ENABLED)))
+	{
+          over = bedata;
+          goto haveoverlay;
+        }
+        if (keyc != over) {
+          TRACE_(layout)("Keycode %d changed to %d (Overlay%d)\n", keyc, over, i);
+        }
+        keyc2over[i-1][keyc] = over;
+    }
+
+    for (over = min_keycode; (over && (over <= max_keycode)); over++) {
+      over2keyc[i-1][over] = over;
+      for (keyc = min_keycode; keyc < over; keyc++) {
+        if (keyc2over[i-1][keyc] == keyc2over[i-1][over]) {
+          over2keyc[i-1][over] = over2keyc[i-1][keyc];
+          break;
+        }
+      }
+      if (over2keyc[i-1][over] != over)
+          TRACE_(layout)("In Overlay%d keycode %d behaves as %d\n",
+                       i, over, over2keyc[i-1][over]);
+    }
   }
+}
+#endif /* HAVE_XKB */
 
-  memset( ckey, 0, sizeof(ckey) );
-  for (keyc = min_keycode; keyc <= max_keycode; keyc++) {
-      /* get data for keycode from X server */
-      for (i = 0; i < syms; i++) {
-        if (!(keysym = XKeycodeToKeysym (display, keyc, i))) continue;
-	/* Allow both one-byte and two-byte national keysyms */
-	if ((keysym < 0x8000) && (keysym != ' '))
-        {
+
+/**********************************************************************
+ *		KeycToOver + OverToKeyc
+ *
+ *    Helper functions that provide direct and inverse
+ *   keycode transformation based on current overlay group  
+ *
+ */
+
+inline static KeyCode
+KeycToOver(KeyCode k){
+#ifdef HAVE_XKB
+    if      (use_xkb && kbd_overlay) return keyc2over[kbd_overlay-1][k];
+    else
+#endif /* HAVE_XKB */
+         return k;
+}
+
+inline static KeyCode
+OverToKeyc(KeyCode k){
 #ifdef HAVE_XKB
-            if (!use_xkb || !XkbTranslateKeySym(display, &keysym, 0, &ckey[keyc][i], 1, NULL))
+    if      (use_xkb && kbd_overlay) return over2keyc[kbd_overlay-1][k];
+    else
+#endif /* HAVE_XKB */
+         return k;
+}
+
+/**********************************************************************
+ *		OverIgnore
+ *
+ *    Should we ignore this keycode when calculating the score
+ *    for layout candidate? When overlay is not active, key that
+ *    is mapped to by some overlay probably is virtual, so must
+ *    not be taken into consideration. When overlay is active,
+ *    we ignore keys that are mapped to other ones or mapped to
+ *    by other overlay (thus it is better to have separate virtual
+ *    keys for Overlay1 and Overlay2).
+ */
+
+inline static Bool
+OverIgnore(KeyCode k){
+#ifdef HAVE_XKB
+    if  (use_xkb) {
+      switch (kbd_overlay) {
+        case 0: return ((over2keyc[0][k] != k)
+	             || (over2keyc[1][k] != k));
+        case 1: return ((keyc2over[0][k] != k)
+	             || (over2keyc[1][k] != k));
+        case 2: return ((keyc2over[1][k] != k)
+	             || (over2keyc[0][k] != k));
+        case 3: return ((keyc2over[1][k] != k)
+	             || (keyc2over[0][k] != k));
+	default: return 0; /* just to make compiler happy */
+      }
+    }
+    else
+#endif /* HAVE_XKB */
+         return 0;
+}
+
+
+/**********************************************************************
+ *		KEYBOARD_CharsForKeycode
+ *
+ *     Build arrays of keysyms and chars that key produces
+ *     depending on shift level. If XKB is used, take it
+ *     directly from keyboard description for the sake of
+ *     speed and correctness
+ */
+
+static Bool
+KEYBOARD_CharsForKeyCode (Display *display,
+                          KeyCode keyc,
+                          KeySym (*ksym)[4],   /*    output      */
+                          char (*chars)[4],    /* return values  */
+                          char (*rawchars)[4], /* other codepage */
+                          int *numchars, int *numrawchars) /* how many  */
+{
+  char Chars[5], RawChars[5];
+  int i, w;
+  KeySym keysym;
+  int NumChars = 0, NumRawChars = 0;
+
+#ifdef HAVE_XKB
+  unsigned info;
+  int group = 0;
+  if (use_xkb) {
+    group = kbd_group;
+    keyc = KeycToOver(keyc);
+    w = XkbKeyNumGroups(desc,keyc);
+    if (!w) {                                                 
+      TRACE_(layout)("No groups for keycode %d\n", keyc);
+      return False;
+    }
+    info = XkbOutOfRangeGroupInfo(XkbKeyGroupInfo(desc,keyc));
+    if (group>=w)
+      switch (info) {
+        case (XkbRedirectIntoRange): group = XkbOutOfRangeGroupNumber(XkbKeyGroupInfo(desc,keyc));
+                                   if (group >= w) group = 0; break;
+        case (XkbClampIntoRange): ;  group = w-1; break;
+        default: group %= w; break;
+      }
+    w = XkbKeyGroupWidth(desc,keyc,group);
+  } else
+#endif /* HAVE_XKB */
+  w = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode;
+
+  for (i = 0; i < w; i++) {
+#ifdef HAVE_XKB
+    if (use_xkb) keysym=XkbKeySymEntry(desc, keyc, i, group);
+    else
 #endif
-            {
-                TRACE("XKB could not translate keysym %ld\n", keysym);
+	keysym = XKeycodeToKeysym (display, keyc, i);
+    if (ksym != NULL) (*ksym)[i] = keysym;
+    if (!keysym) break; /* keysym == NoSymbol == 0 */
+    if ((keysym < 0x8000) && (keysym != ' ')) {
+          RawChars[i] = keysym & 0xFF;
+#ifdef HAVE_XKB
+          if (!use_xkb)
+#endif
+	  Chars[i] = RawChars[i];
+#ifdef HAVE_XKB
+	  else if (!XkbTranslateKeySym(display, &keysym, 0, &Chars[i], 1, NULL))
+          {
+                TRACE_(layout)("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.
                  */
-                ckey[keyc][i] = keysym & 0xFF;
-            }
-        }
-	else {
-	  ckey[keyc][i] = KEYBOARD_MapDeadKeysym(keysym);
-	}
-      }
-  }
+                /* Chars[i] = keysym & 0xFF; */
+                Chars[i] = 0;
+          }
+#endif
+    }
+    else Chars[i] = RawChars[i] = KEYBOARD_MapDeadKeysym(keysym);
 
-  for (current = 0; main_key_tab[current].comment; current++) {
-    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;
-    pkey = -1;
-    for (keyc = min_keycode; keyc <= max_keycode; keyc++) {
-      if (ckey[keyc][0]) {
-	/* 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 (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]))
-	      ok++;
-	    if ((*lkey)[key][i] && ((*lkey)[key][i] != ckey[keyc][i]))
-	      ok = -1;
-	  }
-	  if (ok > 0) {
-	    score += ok;
-	    break;
-	  }
-	}
-	/* count the matches and mismatches */
-	if (ok > 0) {
-	  match++;
-	  /* and how much the keycode order matches */
-	  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 keysym 0x%04lX, keycode %d, got %s\n", keysym, keyc, str );
-          mismatch++;
-          score -= syms;
-	}
-      }
+    if (Chars[i] && (NumChars == i)) {
+	if (chars) (*chars)[i]=Chars[i];
+	NumChars++;
     }
-    TRACE("matches=%d, mismatches=%d, seq=%d, score=%d\n",
-	   match, mismatch, seq, score);
-    if ((score > max_score) ||
-	((score == max_score) && (seq > max_seq))) {
-      /* best match so far */
-      kbd_layout = current;
-      max_score = score;
-      max_seq = seq;
-      ismatch = !mismatch;
+    
+    if (RawChars[i] && (NumRawChars == i)) {
+	if (rawchars) (*rawchars)[i]=RawChars[i];
+	NumRawChars++;
     }
+    
+    if ((NumChars != (i+1)) && (NumRawChars != (i+1))) break;
   }
-  /* we're done, report results if necessary */
-  if (!ismatch)
-    WARN("Using closest match (%s) for scan/virtual codes mapping.\n",
-        main_key_tab[kbd_layout].comment);
 
-  TRACE("detected layout is \"%s\"\n", main_key_tab[kbd_layout].comment);
+  Chars[NumChars]='\0';
+  RawChars[NumRawChars]='\0';
+  if (numchars) (*numchars) = NumChars;
+  if (numrawchars) (*numrawchars) = NumChars;
+  if (chars && (NumChars < 4)) (*chars)[NumChars]='\0';
+  if (rawchars && (NumRawChars < 4)) (*rawchars)[NumRawChars]='\0';
+
+  TRACE_(layout)("Keycode %d translated to %d chars: \"%s\"\n", keyc, NumChars, Chars);
+#ifdef HAVE_XKB
+  if (use_xkb)
+  TRACE_(layout)("              and to %d raw chars: \"%s\"\n", NumRawChars, RawChars);
+#endif
+  if (ksym)  TRACE_(layout)("  Keysyms: %s, %s, %s, %s\n",
+            XKeysymToString((*ksym)[0]), XKeysymToString((*ksym)[1]),
+            XKeysymToString((*ksym)[2]), XKeysymToString((*ksym)[3]));
+
+  if (NumChars || NumRawChars) return True;
+  else return False;
 }
 
 /**********************************************************************
- *		X11DRV_InitKeyboard
+ *		KEYBOARD_DetectLayouts
+ *
+ *     Detect all layouts (1 without XKB, up to 16 if XKB is used)
+ *     Make the list of preferred XKB and overlay groups for all
+ *     layouts ( layout can be detected two or more times, so we should
+ *     find the closest match for ActivateKeyboardLayout() )
  */
-void X11DRV_InitKeyboard(void)
+
+static void
+KEYBOARD_DetectLayouts (void)
 {
-    Display *display = thread_display();
-    KeySym *ksp;
-    XModifierKeymap *mmp;
-    KeySym keysym;
-    KeyCode *kcp;
-    XKeyEvent e2;
-    WORD scan, vkey, OEMvkey;
-    int keyc, i, keyn, syms;
-    char ckey[4]={0,0,0,0};
-    const char (*lkey)[MAIN_LEN][4];
+/* enter in tsx11_lock()'ed state */
 
-    wine_tsx11_lock();
-    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);
-
-    mmp = XGetModifierMapping(display);
-    kcp = mmp->modifiermap;
-    for (i = 0; i < 8; i += 1) /* There are 8 modifier keys */
-    {
+  Display *display = thread_display();
+  int current, key, i, ok, have_chars, have_raw;
+  int score, match, miss, inv, is_raw;
+  int prev, bestScore, bestInv, bestMiss;
+  int prefLayout, prefCP, prefScore;
+  WORD scan, vkey;
+
+  const char (*lkey)[MAIN_LEN][4];
+  static char keyc2char[2][256][4] = {{{0}}}; /* 0 = locale, 1 = raw */
+  unsigned int codepage;
+  KeyCode keyc;
+  KeySym keysym;
+  XModifierKeymap *mmp;
+  KeyCode * kcp;
+
+#ifdef HAVE_XKB
+  int save_group = 0, save_overlay = 0;
+
+  if (use_xkb) { /* get XKB keyboard description to speed up things later */
+    desc = XkbGetKeyboard(display,
+           XkbAllComponentsMask,
+           XkbUseCoreKbd);
+/*    XkbGetControls(display, XkbAllControlsMask, desc); */
+    save_group   = kbd_group;
+    save_overlay = kbd_overlay;
+  } else
+#endif /* HAVE_XKB */
+  {
+    TRACE("No XKB, keycodes from %d to %d\n",
+          min_keycode, max_keycode);
+  }
+
+  /* Everything from the beginning */
+    
+#ifdef HAVE_XKB
+  if (use_xkb) KEYBOARD_RefreshOverlays();
+#endif
+
+  mmp = XGetModifierMapping(display);
+  kcp = mmp->modifiermap;
+  for (i = 0; i < 8; i += 1) /* There are 8 modifier keys */
+  {
         int j;
 
         for (j = 0; j < mmp->max_keypermod; j += 1, kcp += 1)
@@ -1585,33 +2014,184 @@
                     if (XKeycodeToKeysym(display, *kcp, k) == XK_Num_Lock)
 		    {
                         NumLockMask = 1 << i;
-                        TRACE_(key)("NumLockMask is %x\n", NumLockMask);
+                        TRACE_(layout)("NumLockMask is %x\n", NumLockMask);
 		    }
             }
+  }
+  XFreeModifiermap(mmp);
+
+  /* Now store one keycode for each modifier. Used to simulate keypresses. */
+  kcControl = XKeysymToKeycode(display, XK_Control_L);
+  kcAlt = XKeysymToKeycode(display, XK_Alt_L);
+  if (!kcAlt) kcAlt = XKeysymToKeycode(display, XK_Meta_L);
+  kcShift = XKeysymToKeycode(display, XK_Shift_L);
+  kcNumLock = XKeysymToKeycode(display, XK_Num_Lock);
+  kcCapsLock = XKeysymToKeycode(display, XK_Caps_Lock);
+
+  global_refresh = 0;  /* finished */
+
+  /* Going through all possible groups and overlays */
+#ifdef HAVE_XKB
+  for (layout_no=0; layout_no<(use_xkb? MAX_OVERLAYS*max_group:1); layout_no++) {
+  if (use_xkb) {
+      kbd_group   = layout_no / MAX_OVERLAYS;
+      kbd_overlay = layout_no % MAX_OVERLAYS;
+      TRACE("examining layout #%d : XKB group %d, overlay %d, keycodes from %d to %d\n",
+          layout_no, kbd_group, kbd_overlay, min_keycode, max_keycode);
+  }
+#else
+  for (layout_no=0; 0;) {
+#endif
+
+  for (key=0; key<256; key++) {
+    keyc2vkey[layout_no][key] = 0; keyc2scan[layout_no][key] = 0;
+    for (i=0; i<4; i++) {
+      keyc2ksym[layout_no][key][i] = 0;
+      keyc2char[0][key][i] = keyc2char[1][key][i] ='\0';
     }
-    XFreeModifiermap(mmp);
+  }
 
-    /* Detect the keyboard layout */
-    X11DRV_KEYBOARD_DetectLayout();
-    lkey = main_key_tab[kbd_layout].key;
-    syms = (keysyms_per_keycode > 4) ? 4 : keysyms_per_keycode;
+  for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+     if (!KEYBOARD_CharsForKeyCode (display,
+                                           keyc, &keyc2ksym[layout_no][keyc],
+                                           &keyc2char[0][keyc], &keyc2char[1][keyc],
+					   &have_chars, &have_raw)) {
+#ifdef HAVE_XKB
+       if (use_xkb)
+         TRACE_(layout)("No keysyms translated in layout %d for keycode %d\n", layout_no, keyc);
+       else
+#endif
+         TRACE_(layout)("No keysyms translated for keycode %d\n", keyc);
+     }
+
+  bestScore = prefScore = 0; bestInv = max_keycode; bestMiss = MAIN_LEN; prev = 0;
+  prefLayout = kbd_layout[layout_no]; /* May be set in registry */
+  prefCP     = kbd_cp[layout_no];
+  kbd_layout[layout_no] = 0; /* Fallback */
+  kbd_list[layout_no] = 0;
+  kbd_cp[layout_no] = 0;
+  for (current = 0; main_key_tab[current].comment; current++) {
+       is_raw = 0; /* first try locale encoding */
+    again:
+#ifdef HAVE_XKB
+       if (is_raw) {
+	      if (!(codepage=main_key_tab[current].codepage))
+		continue;
+       } else codepage = 0;
+#endif
+       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 (!keyc2char[is_raw][keyc][0] || OverIgnore(keyc)) continue;
+	 for (key = 0; key < MAIN_LEN; key++) {
+	  for (ok = i = 0; ((ok >= 0) && ((*lkey)[key][i])); i++) {
+	    if ((*lkey)[key][i] == keyc2char[is_raw][keyc][i])  ok++;
+	    else ok = -1;
+	  }
+	  if (ok > 0) {
+	    score += ok;
+	    break;
+	  }
+	 }
+	/* count the matches and mismatches */
+	 if (ok > 0) {
+	   match++;
+/*           TRACE_(layout)("keycode %d matches entry %d\n", keyc, key); */
+	  /* and how much the keycode order matches */
+	   if (key < prev) inv++;
+	   prev = key;
+	 }
+         else {
+/*           TRACE_(layout)("mismatch for keycode %d\n", keyc); */
+	   miss++;
+           for (i=0; i<4; i++) {
+            if (keyc2char[is_raw][keyc][i]) score--;
+            else break;
+           }
+	 }
+      }
+      TRACE_(layout)("\"%s\", %s lookup: matches=%d, mismatches=%d, inversions=%d, score=%d\n",
+           main_key_tab[current].comment,
+	   (is_raw?"raw":"locale"),
+           match, miss, inv, score);
+      if (prefLayout == current) prefScore = score;
+      if ((score > bestScore) ||
+         ((score == bestScore) && (!is_raw) && (kbd_cp[layout_no])) || 
+         ((score == bestScore) && ((!is_raw) == (!kbd_cp[layout_no])) && (inv<bestInv))) 
+      {
+           bestScore  = score;
+           bestInv    = inv;
+           bestMiss   = miss;
+           kbd_layout[layout_no] = current;
+#ifdef HAVE_XKB
+	   kbd_cp[layout_no] = codepage;
+      }
+      if (use_xkb && !is_raw) {
+           is_raw = 1;
+	   goto again; /* now simply get lower bytes */
+#endif
+      }
+  }
 
-    /* Now build two conversion arrays :
-     * keycode -> vkey + scancode + extended
-     * vkey + extended -> keycode */
+  if (bestMiss) {
+    if (TRACE_ON(keyboard))
+      TRACE("Detected layout is \"%s\" (closest match), codepage %d\n",
+                     main_key_tab[kbd_layout[layout_no]].comment,
+		     kbd_cp[layout_no]);
+    else
+      TRACE_(layout)("Detected layout is \"%s\" (closest match), codepage %d\n",
+                     main_key_tab[kbd_layout[layout_no]].comment,
+		     kbd_cp[layout_no]);
+  }
+  else {
+    if (TRACE_ON(keyboard))
+      TRACE("Detected layout is \"%s\" (exact match), codepage %d\n",
+                     main_key_tab[kbd_layout[layout_no]].comment,
+		     kbd_cp[layout_no]);
+    else
+      TRACE_(layout)("Detected layout is \"%s\" (exact match), codepage %d\n",
+                     main_key_tab[kbd_layout[layout_no]].comment,
+		     kbd_cp[layout_no]);
+  }
 
-    e2.display = display;
-    e2.state = 0;
+  if (prefLayout != -1) {
+    	TRACE("Overridden by registry settings, layout is \"%s\", codepage %d,\n",
+                     main_key_tab[prefLayout].comment,
+		     prefCP);
+	if (prefScore < bestScore)
+    	    TRACE("                                 score %d (best is %d)\n",
+                     prefScore, bestScore);
+	kbd_layout[layout_no] = prefLayout;
+	kbd_cp[layout_no] = prefCP;
+	bestScore = prefScore;
+  }
 
-    OEMvkey = VK_OEM_8; /* next is available.  */
-    for (keyc = min_keycode; keyc <= max_keycode; keyc++)
-    {
-        char buf[30];
-        int have_chars;
+#ifdef HAVE_XKB
+  if (use_xkb && bestScore) {
+      kbd_list[layout_no] = bestScore;
+      for (i=0; i<layout_no; i++) {
+        if (kbd_list[i] && (main_key_tab[kbd_layout[i]].lcid ==
+	    main_key_tab[kbd_layout[layout_no]].lcid)) {
+	  if (kbd_list[layout_no] > kbd_list[i])
+	    kbd_list[i] = 0;
+	  else
+	    kbd_list[layout_no] = 0;
+	  break;
+	}
+      }
+  }
+#endif /* HAVE_XKB */
 
-        keysym = 0;
-        e2.keycode = (KeyCode)keyc;
-        have_chars = XLookupString(&e2, buf, sizeof(buf), &keysym, NULL);
+  for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+  {
+        keysym = keyc2ksym[layout_no][keyc][0];
+	is_raw = (kbd_cp[layout_no] != 0);
+        have_chars = (keyc2char[is_raw][keyc][0]);
         vkey = 0; scan = 0;
         if (keysym)  /* otherwise, keycode not used */
         {
@@ -1626,116 +2206,51 @@
 		scan = 0x39;
 	    } else if (have_chars) {
 	      /* we seem to need to search the layout-dependent scancodes */
-	      int maxlen=0,maxval=-1,ok;
-	      for (i=0; i<syms; i++) {
-		keysym = XKeycodeToKeysym(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 & 0xFF;
-                    }
-		} else {
-		  ckey[i] = KEYBOARD_MapDeadKeysym(keysym);
-		}
-	      }
+	      lkey = main_key_tab[kbd_layout[layout_no]].key;
+	      int maxlen=0,maxval=-1;
 	      /* 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 (ok||(i>maxlen)) {
-		  maxlen=i; maxval=keyn;
+	      for (key=0; key<MAIN_LEN; key++) {
+                for(i=0, ok=1; i<4; i++) {
+                  if (!(*lkey)[key][i]) break;
+		  if ((*lkey)[key][i]!=keyc2char[is_raw][keyc][i]) {
+                   ok=0; break;
+                  }
+                }
+		if ((ok && i)||(i>maxlen)) {
+		  maxlen=i; maxval=key;
 		}
 		if (ok) break;
 	      }
 	      if (maxval>=0) {
 		/* got it */
-		const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout].scan;
-		const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout].vkey;
+		const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout[layout_no]].scan;
+		const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout[layout_no]].vkey;
 		scan = (*lscan)[maxval];
 		vkey = (*lvkey)[maxval];
+                unsigned int out[4];
+                char bkey[5];
+                out[0] = (unsigned char)keyc2char[is_raw][keyc][0];
+                out[1] = (unsigned char)keyc2char[is_raw][keyc][1];
+                out[2] = (unsigned char)keyc2char[is_raw][keyc][2];
+                out[3] = (unsigned char)keyc2char[is_raw][keyc][3];
+                memccpy(&bkey[0], keyc2char[is_raw][keyc], 0, 4);
+		bkey[5] = '\0';
+                TRACE_(layout)("Identified %d chars of \"%s\" / "
+                            "\"%02x,%02x,%02x,%02x\" as \"%s\", entry %d\n",
+                            maxlen, bkey, out[0], out[1], out[2], out[3],
+			    (*lkey)[key], maxval); 
 	      }
 	    }
-#if 0 /* this breaks VK_OEM_x VKeys in some layout tables by inserting
-       * a VK code into a not appropriate place.
-       */
-            /* find a suitable layout-dependent VK code */
-	    /* (most Winelib apps ought to be able to work without layout tables!) */
-            for (i = 0; (i < keysyms_per_keycode) && (!vkey); i++)
-            {
-                keysym = XLookupKeysym(&e2, i);
-                if ((keysym >= VK_0 && keysym <= VK_9)
-                    || (keysym >= VK_A && keysym <= VK_Z)) {
-		    vkey = keysym;
-		}
-            }
 
-            for (i = 0; (i < keysyms_per_keycode) && (!vkey); i++)
-            {
-                keysym = XLookupKeysym(&e2, i);
-		switch (keysym)
-		{
-		case ';':             vkey = VK_OEM_1; break;
-		case '/':             vkey = VK_OEM_2; break;
-		case '`':             vkey = VK_OEM_3; break;
-		case '[':             vkey = VK_OEM_4; break;
-		case '\\':            vkey = VK_OEM_5; break;
-		case ']':             vkey = VK_OEM_6; break;
-		case '\'':            vkey = VK_OEM_7; break;
-		case ',':             vkey = VK_OEM_COMMA; break;
-		case '.':             vkey = VK_OEM_PERIOD; break;
-		case '-':             vkey = VK_OEM_MINUS; break;
-		case '+':             vkey = VK_OEM_PLUS; break;
-		}
-	    }
-
-            if (!vkey)
-            {
-                /* Others keys: let's assign OEM virtual key codes in the allowed range,
-                 * that is ([0xba,0xc0], [0xdb,0xe4], 0xe6 (given up) et [0xe9,0xf5]) */
-                switch (++OEMvkey)
-                {
-                case 0xc1 : OEMvkey=0xdb; break;
-                case 0xe5 : OEMvkey=0xe9; break;
-                case 0xf6 : OEMvkey=0xf5; WARN("No more OEM vkey available!\n");
-                }
-
-                vkey = OEMvkey;
-
-                if (TRACE_ON(keyboard))
-                {
-                    TRACE("OEM specific virtual key %X assigned to keycode %X:\n",
-                                     OEMvkey, e2.keycode);
-                    TRACE("(");
-                    for (i = 0; i < keysyms_per_keycode; i += 1)
-                    {
-                        const char *ksname;
-
-                        keysym = XLookupKeysym(&e2, i);
-                        ksname = XKeysymToString(keysym);
-                        if (!ksname)
-			    ksname = "NoSymbol";
-                        TRACE( "%lX (%s) ", keysym, ksname);
-                    }
-                    TRACE(")\n");
-                }
-            }
-#endif
         }
-        TRACE("keycode %04x => vkey %04x\n", e2.keycode, vkey);
-        keyc2vkey[e2.keycode] = vkey;
-        keyc2scan[e2.keycode] = scan;
-    } /* for */
+        TRACE_(layout)("keycode %04x => vkey %04x\n", keyc, vkey);
+        keyc2vkey[layout_no][keyc] = vkey;
+        keyc2scan[layout_no][keyc] = scan;
+  } /* for */
 
     /* If some keys still lack scancodes, assign some arbitrary ones to them now */
-    for (scan = 0x60, keyc = min_keycode; keyc <= max_keycode; keyc++)
-      if (keyc2vkey[keyc]&&!keyc2scan[keyc]) {
+  for (scan = 0x60, keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+      if (keyc2vkey[layout_no][keyc]&&!keyc2scan[layout_no][keyc]) {
 	const char *ksname;
 	keysym = XKeycodeToKeysym(display, keyc, 0);
 	ksname = XKeysymToString(keysym);
@@ -1743,17 +2258,114 @@
 
 	/* 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[keyc]=scan++;
+	TRACE_(layout)("assigning scancode %02x to unidentified keycode %02x (%s)\n",scan,keyc,ksname);
+	keyc2scan[layout_no][keyc]=scan++;
       }
 
-    /* Now store one keycode for each modifier. Used to simulate keypresses. */
-    kcControl = XKeysymToKeycode(display, XK_Control_L);
-    kcAlt = XKeysymToKeycode(display, XK_Alt_L);
-    if (!kcAlt) kcAlt = XKeysymToKeycode(display, XK_Meta_L);
-    kcShift = XKeysymToKeycode(display, XK_Shift_L);
-    kcNumLock = XKeysymToKeycode(display, XK_Num_Lock);
-    kcCapsLock = XKeysymToKeycode(display, XK_Caps_Lock);
+  } /* End of grand cycle, restore current group and overlay */
+#ifdef HAVE_XKB
+  if (use_xkb) {
+    for (layout_no=0; layout_no<(use_xkb? MAX_OVERLAYS*max_group:1);
+         layout_no++) {
+	 if (kbd_list[layout_no]) {
+	   TRACE("Preferred layout #%d \"%s\", score %d, codepage %d\n", layout_no,
+	        main_key_tab[kbd_layout[layout_no]].comment,
+		kbd_list[layout_no],
+		kbd_cp[layout_no]);
+	 }
+    }
+    if (desc) {
+      XkbFreeKeyboard(desc, XkbAllComponentsMask,True);
+      desc = NULL;
+    }
+    kbd_group   = save_group;
+    kbd_overlay = save_overlay;
+    layout_no = MAX_OVERLAYS*kbd_group + kbd_overlay;
+  } else
+#endif
+  layout_no = 0;
+
+/* exit in tsx11_lock()'ed state again */
+}
+
+/**********************************************************************
+ *		InitKeyboard (X11DRV.@)
+ */
+void X11DRV_InitKeyboard(void)
+{
+    int	current, no;
+    const int buf_size = 128;
+    unsigned int cp; 
+    DWORD type, count;
+    char buffer[buf_size], name_buffer[12];
+    HKEY hkey;
+
+    /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver\Keyboard */
+    if (RegOpenKeyA(HKEY_CURRENT_USER, INIKeyboard, &hkey)) hkey = 0;
+#ifdef HAVE_XKB
+    for (no=0; no<(use_xkb?MAX_LAYOUTS:1); no++ ) {
+#else
+    for (no=0; 0;) {
+#endif
+	kbd_layout[no] = -1;
+	kbd_cp[no] = cp =  0;
+	if (!hkey) continue;
+	sprintf(name_buffer, "Layout%d", no);
+	count = buf_size;
+	buffer[0] = 0;
+	RegQueryValueExA(hkey, name_buffer, 0, &type, (LPBYTE)buffer, &count);
+	if (!buffer[0]) continue;
+	for (current=0; main_key_tab[current].comment; current++)
+	    if (!strcmp(buffer,main_key_tab[current].comment)) break;
+	if (!main_key_tab[current].comment) {
+	    TRACE("Layout #%d \"%s\" specified in registry but not found\n",
+		no, buffer);
+	    continue;
+	}
+	TRACE("Layout #%d \"%s\" specified in registry\n", no, buffer);
+	kbd_layout[no] = current;
+#ifdef HAVE_XKB
+	if (use_xkb) {
+	    count = buf_size;
+	    buffer[0] = 0;
+	    sprintf(name_buffer, "Codepage%d", no);
+	    RegQueryValueExA(hkey, name_buffer, 0, &type, (LPBYTE)buffer, &count);
+	    if (buffer[0]) cp=atoi(buffer);
+	    if (cp>0) {
+	      kbd_cp[no] = cp;
+	      TRACE("Codepage %d for layout #%d specified in registry\n", cp, no);
+	    }
+	}
+#endif
+    }
+    if (hkey) RegCloseKey(hkey);
+
+#ifdef HAVE_XKB
+    if (use_xkb)
+     for (current = 0; main_key_tab[current].comment; current++) {
+       if (!use_xkb) continue;
+       if (main_key_tab[current].codepage)
+		TRACE_(layout)("keysyms codepage for %s defaults to %d\n",
+		    main_key_tab[current].comment,main_key_tab[current].codepage);
+       else {
+	      if (!GetLocaleInfoW(main_key_tab[current].lcid,
+		LOCALE_IDEFAULTUNIXCODEPAGE|LOCALE_RETURN_NUMBER,
+		(WCHAR *)(&cp),sizeof(cp)/sizeof(WCHAR))) {
+		TRACE_(layout)("keysyms codepage for %s unknown, cannot get DefaultUnixCodepage, skipping\n",
+		    main_key_tab[current].comment);
+	      } else {
+		TRACE_(layout)("set keysyms codepage for %s to DefaultUnixCodepage = %d\n",
+		    main_key_tab[current].comment,cp);
+	        main_key_tab[current].codepage = cp;
+	      }
+       }
+     }
+#endif
+    wine_tsx11_lock();
+    
+    KEYBOARD_LayoutSwitched(); /* get current group and overlay */
+    KEYBOARD_DetectLayouts(); /* detect all layouts */
+
     wine_tsx11_unlock();
 }
 
@@ -1818,8 +2430,8 @@
     if (dwThreadid && dwThreadid != GetCurrentThreadId())
         FIXME("couldn't return keyboard layout for thread %04x\n", dwThreadid);
 
-#if 0
-    layout = main_key_tab[kbd_layout].lcid;
+#if 1
+    layout = main_key_tab[kbd_layout[layout_no]].lcid;
 #else
     /* FIXME:
      * Winword uses return value of GetKeyboardLayout as a codepage
@@ -1855,7 +2467,8 @@
     DWORD layout;
     LANGID langid;
 
-    layout = main_key_tab[kbd_layout].lcid;
+    layout = main_key_tab[kbd_layout[layout_no]].lcid;
+
     /* see comment for GetKeyboardLayout */
     langid = PRIMARYLANGID(LANGIDFROMLCID(layout));
     if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
@@ -1896,9 +2509,72 @@
  */
 HKL X11DRV_ActivateKeyboardLayout(HKL hkl, UINT flags)
 {
-    FIXME("%p, %04x: stub!\n", hkl, flags);
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return 0;
+#ifdef HAVE_XKB
+      /* Ignore all flags : */
+      /* KLF_REORDER, KLF_RESET, KLF_SETFORPROCESS, KLF_SHIFTLOCK */
+      /* KLF_UNLOADPREVIOUS - unsupported by Windows */
+    if (use_xkb) {
+      HKL cur_lcid;
+      ULONG_PTR lcid;
+      int i;
+      Display *display;
+      wine_tsx11_lock();
+      display = thread_display();
+      lcid = (ULONG_PTR)hkl;
+      cur_lcid = (HKL)(main_key_tab[kbd_layout[layout_no]].lcid);
+      if (lcid == HKL_NEXT) {
+        for(i=layout_no+1; i != layout_no; i++) {
+	  if (i == MAX_OVERLAYS*max_group) i=0;
+	  if (kbd_list[i]) break;
+	}
+	if (i == layout_no) {
+	  TRACE("Failed to activate next layout\n");
+          wine_tsx11_unlock();
+	  return 0;
+	}
+      } else
+      if (lcid == HKL_PREV) {
+        for(i=layout_no-1; i != layout_no; i--) {
+	  if (i < 0) i=MAX_OVERLAYS*max_group-1;
+	  if (kbd_list[i]) break;
+	}
+	if (i == layout_no) {
+	  TRACE("Failed to activate previous layout\n");
+          wine_tsx11_unlock();
+	  return 0;
+	}
+      } else {
+        if (main_key_tab[kbd_layout[layout_no]].lcid == lcid) {
+ 	  TRACE("Handle %08lx is already active\n",lcid);
+          wine_tsx11_unlock();
+	  return hkl;
+	}
+        for(i=0; i<MAX_OVERLAYS*max_group; i++)
+	  if (kbd_list[i] && 
+	   (main_key_tab[kbd_layout[i]].lcid == lcid)) break;
+	if (i == MAX_OVERLAYS*max_group) {
+	  TRACE("Failed to activate handle %08lx\n",lcid);
+          wine_tsx11_unlock();
+	  return 0;
+	}
+      }
+      XkbLockGroup(display, XkbUseCoreKbd, i/MAX_OVERLAYS);
+      XkbChangeEnabledControls(display, XkbUseCoreKbd, 
+	   XkbOverlay1Mask | XkbOverlay2Mask ,
+	   (((i%MAX_OVERLAYS) & OVER1_ENABLED ?  XkbOverlay1Mask : 0) |
+	    ((i%MAX_OVERLAYS) & OVER2_ENABLED ?  XkbOverlay2Mask : 0)));
+/*    KEYBOARD_LayoutSwitched(); // Should we wait for server response ? */
+      TRACE(" #%d, \"%s\", handle %08lx\n",
+         layout_no, main_key_tab[kbd_layout[layout_no]].comment, lcid);
+      wine_tsx11_unlock();
+      return cur_lcid;
+    } else
+#endif /* HAVE_XKB */
+    {
+      FIXME("%p, %04x: stub: not implemented without XKB!\n", hkl, flags);
+      SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+      return 0;
+    }
 }
 
 
@@ -1938,13 +2614,24 @@
     /* FIXME: what happens if wChar is not a Latin1 character and CP_UNIXCP
      * is UTF-8 (multibyte encoding)?
      */
-    if (!WideCharToMultiByte(CP_UNIXCP, 0, &wChar, 1, &cChar, 1, NULL, NULL))
+#ifdef HAVE_XKB
+    if (use_xkb && kbd_cp[layout_no]) {
+	if (!WideCharToMultiByte(kbd_cp[layout_no], 0, &wChar, 1, &cChar, 1, NULL, NULL))
+	{
+    	    WARN("no translation from unicode to codepage %d for 0x%02x\n", kbd_cp[layout_no], wChar);
+    	    return -1;
+	}
+    } else 
+#endif
     {
-        WARN("no translation from unicode to CP_UNIXCP for 0x%02x\n", wChar);
-        return -1;
+	if (!WideCharToMultiByte(CP_UNIXCP, 0, &wChar, 1, &cChar, 1, NULL, NULL))
+	{
+    	    WARN("no translation from unicode to CP_UNIXCP for 0x%02x\n", wChar);
+    	    return -1;
+	}
     }
 
-    TRACE("wChar 0x%02x -> cChar '%c'\n", wChar, cChar);
+    TRACE_(key)("wChar 0x%02x -> cChar '%c'\n", wChar, cChar);
 
     /* char->keysym (same for ANSI chars) */
     keysym = (unsigned char)cChar; /* (!) cChar is signed */
@@ -1957,45 +2644,42 @@
         if (keysym >= 0xFF00) /* Windows returns 0x0240 + cChar in this case */
         {
             ret = 0x0240 + cChar; /* 0x0200 indicates a control character */
-            TRACE(" ... returning ctrl char %#.2x\n", ret);
+            TRACE_(key)(" ... returning ctrl char %#.2x\n", ret);
             wine_tsx11_unlock();
             return ret;
         }
         /* It didn't work ... let's try with deadchar code. */
-        TRACE("retrying with | 0xFE00\n");
+        TRACE_(key)("retrying with | 0xFE00\n");
         keycode = XKeysymToKeycode(display, keysym | 0xFE00);
     }
     wine_tsx11_unlock();
 
-    TRACE("'%c'(%#lx, %lu): got keycode %#.2x (%d)\n",
+    TRACE_(key)("'%c'(%#lx, %lu): got keycode %#.2x (%d)\n",
             cChar, keysym, keysym, keycode, keycode);
 
     /* keycode -> (keyc2vkey) vkey */
-    ret = keyc2vkey[keycode];
+    ret = keyc2vkey[layout_no][keycode];
 
     if (!keycode || !ret)
     {
-        TRACE("keycode for '%c' not found, returning -1\n", cChar);
+        TRACE_(key)("keycode for '%c' not found, returning -1\n", cChar);
         return -1;
     }
 
     index = -1;
-    wine_tsx11_lock();
     for (i = 0; i < 4; i++) /* find shift state */
     {
-        if (XKeycodeToKeysym(display, keycode, i) == keysym)
-        {
-            index = i;
-            break;
-        }
+      if (keyc2ksym[layout_no][keycode][i] && (keyc2ksym[layout_no][keycode][i] == keysym)) {
+         index = i;
+         break;
+      }
     }
-    wine_tsx11_unlock();
 
     switch (index)
     {
         default:
         case -1:
-            WARN("Keysym %lx not found while parsing the keycode table\n", keysym);
+            WARN_(key)("Keysym %lx not found while parsing the keycode table\n", keysym);
             return -1;
 
         case 0: break;
@@ -2011,7 +2695,7 @@
       index : 3     adds 0x0700 (ctrl+alt+shift)
      */
 
-    TRACE(" ... returning %#.2x\n", ret);
+    TRACE_(key)(" ... returning %#.2x\n", ret);
     return ret;
 }
 
@@ -2024,7 +2708,7 @@
 
 #define returnMVK(value) { TRACE("returning 0x%x.\n",value); return value; }
 
-    TRACE("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
+    TRACE_(key)("wCode=0x%x, wMapType=%d, hkl %p\n", wCode, wMapType, hkl);
     if (hkl != X11DRV_GetKeyboardLayout(0))
         FIXME("keyboard layout %p is not supported\n", hkl);
 
@@ -2033,18 +2717,18 @@
 			/* let's do vkey -> keycode -> scan */
 			int keyc;
 			for (keyc=min_keycode; keyc<=max_keycode; keyc++)
-				if ((keyc2vkey[keyc] & 0xFF) == wCode)
-					returnMVK (keyc2scan[keyc] & 0xFF);
-			TRACE("returning no scan-code.\n");
+				if ((keyc2vkey[layout_no][keyc] & 0xFF) == wCode)
+					returnMVK (keyc2scan[layout_no][keyc] & 0xFF);
+			TRACE_(key)("returning no scan-code.\n");
 		        return 0; }
 
 		case 1: { /* scan-code to vkey-code */
 			/* let's do scan -> keycode -> vkey */
 			int keyc;
 			for (keyc=min_keycode; keyc<=max_keycode; keyc++)
-				if ((keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
-					returnMVK (keyc2vkey[keyc] & 0xFF);
-			TRACE("returning no vkey-code.\n");
+				if ((keyc2scan[layout_no][keyc] & 0xFF) == (wCode & 0xFF))
+					returnMVK (keyc2vkey[layout_no][keyc] & 0xFF);
+			TRACE_(key)("returning no vkey-code.\n");
 		        return 0; }
 
 		case 2: { /* vkey-code to unshifted ANSI code */
@@ -2056,7 +2740,7 @@
                          */
 			/* let's do vkey -> keycode -> (XLookupString) ansi char */
 			XKeyEvent e;
-			KeySym keysym;
+			KeySym keysym = 0;
 			int keyc;
 			char s[2];
 			e.display = display;
@@ -2071,13 +2755,18 @@
 			/* 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 */
-			    if  ((keyc2vkey[keyc] & 0xFF) == wCode)
+			    if  ((keyc2vkey[layout_no][keyc] & 0xFF) == wCode)
 			    { /* 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 */
-				}
+                                e.keycode = KeycToOver(keyc); /* Store it temporarily */
+				if ( keyc != e.keycode )
+                                   TRACE_(key)("Keycode %02x turned back to %02x\n", keyc, e.keycode);
+                                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 */
+                                }
+                                else {
+                                    e.keycode = KeycToOver(keyc); /* again */
+                                }
 			    }
 			}
 
@@ -2089,19 +2778,27 @@
 
 			if (!e.keycode)
 			{
-			  WARN("Unknown virtual key %X !!!\n", wCode);
+			  WARN_(key)("Unknown virtual key %X !!!\n", wCode);
                           wine_tsx11_unlock();
 			  return 0; /* whatever */
 			}
-			TRACE("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
+			TRACE_(key)("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
 
-			if (XLookupString(&e, s, 2, &keysym, NULL))
+			if (XLookupString(&e, s, 2, &keysym, NULL) && !kbd_cp[layout_no])
                         {
                             wine_tsx11_unlock();
                             returnMVK (*s);
-                        }
-
-			TRACE("returning no ANSI.\n");
+                        } 
+#ifdef HAVE_XKB
+			else if (keysym && kbd_cp[layout_no]) {
+			    KeySym lkeysym, ukeysym;
+			    XConvertCase(keysym,&lkeysym,&ukeysym);
+                            wine_tsx11_unlock();
+			    if (ukeysym < 0x8000)
+                        	returnMVK ((unsigned)(ukeysym & 0xFF)); /* ???? */
+			}
+#endif
+			TRACE_(key)("returning no ANSI.\n");
                         wine_tsx11_unlock();
 			return 0;
 			}
@@ -2112,7 +2809,7 @@
                           return 0;
 
 		default: /* reserved */
-			WARN("Unknown wMapType %d !\n", wMapType);
+			WARN_(key)("Unknown wMapType %d !\n", wMapType);
 			return 0;
 	}
 	return 0;
@@ -2156,7 +2853,7 @@
   }
 
   ansi = X11DRV_MapVirtualKeyEx(vkey, 2, X11DRV_GetKeyboardLayout(0));
-  TRACE("scan 0x%04x, vkey 0x%04x, ANSI 0x%04x\n", scanCode, vkey, ansi);
+  TRACE_(key)("scan 0x%04x, vkey 0x%04x, ANSI 0x%04x\n", scanCode, vkey, ansi);
 
   /* first get the name of the "regular" keys which is the Upper case
      value of the keycap imprint.                                     */
@@ -2190,7 +2887,7 @@
   /* let's do scancode -> keycode -> keysym -> String */
 
   for (keyi=min_keycode; keyi<=max_keycode; keyi++)
-      if ((keyc2scan[keyi]) == scanCode)
+      if ((keyc2scan[layout_no][keyi]) == scanCode)
          break;
   if (keyi <= max_keycode)
   {
@@ -2199,7 +2896,7 @@
       keys = XKeycodeToKeysym(thread_display(), keyc, 0);
       name = XKeysymToString(keys);
       wine_tsx11_unlock();
-      TRACE("found scan=%04x keyc=%04x keysym=%04x string=%s\n",
+      TRACE_(key)("found scan=%04x keyc=%04x keysym=%04x string=%s\n",
             scanCode, keyc, (int)keys, name);
       if (lpBuffer && nSize && name)
       {
@@ -2291,7 +2988,6 @@
 	        return 's';
 */
 	    }
-	TRACE("no character for dead keysym 0x%08lx\n",keysym);
 	return 0;
 }
 
@@ -2320,7 +3016,8 @@
     XKeyEvent e;
     KeySym keysym = 0;
     INT ret;
-    int keyc;
+    int keyc, savetype;
+    unsigned int savestate;
     char lpChar[10];
     HWND focus;
     XIC xic;
@@ -2328,7 +3025,7 @@
 
     if (scanCode & 0x8000)
     {
-        TRACE("Key UP, doing nothing\n" );
+        TRACE_(key)("Key UP, doing nothing\n" );
         return 0;
     }
 
@@ -2337,7 +3034,7 @@
 
     if ((lpKeyState[VK_MENU] & 0x80) && (lpKeyState[VK_CONTROL] & 0x80))
     {
-        TRACE("Ctrl+Alt+[key] won't generate a character\n");
+        TRACE_(key)("Ctrl+Alt+[key] won't generate a character\n");
         return 0;
     }
 
@@ -2354,27 +3051,27 @@
 
     if (lpKeyState[VK_SHIFT] & 0x80)
     {
-	TRACE("ShiftMask = %04x\n", ShiftMask);
+	TRACE_(key)("ShiftMask = %04x\n", ShiftMask);
 	e.state |= ShiftMask;
     }
     if (lpKeyState[VK_CAPITAL] & 0x01)
     {
-	TRACE("LockMask = %04x\n", LockMask);
+	TRACE_(key)("LockMask = %04x\n", LockMask);
 	e.state |= LockMask;
     }
     if (lpKeyState[VK_CONTROL] & 0x80)
     {
-	TRACE("ControlMask = %04x\n", ControlMask);
+	TRACE_(key)("ControlMask = %04x\n", ControlMask);
 	e.state |= ControlMask;
     }
     if (lpKeyState[VK_NUMLOCK] & 0x01)
     {
-	TRACE("NumLockMask = %04x\n", NumLockMask);
+	TRACE_(key)("NumLockMask = %04x\n", NumLockMask);
 	e.state |= NumLockMask;
     }
 
     /* Restore saved AltGr state */
-    TRACE("AltGrMask = %04x\n", AltGrMask);
+    TRACE_(key)("AltGrMask = %04x\n", AltGrMask);
     e.state |= AltGrMask;
 
     TRACE_(key)("(%04X, %04X) : faked state = 0x%04x\n",
@@ -2383,13 +3080,18 @@
     /* 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 */
-          if  ((keyc2vkey[keyc] & 0xFF) == virtKey)
+          if  ((keyc2vkey[layout_no][keyc] & 0xFF) == virtKey)
           { /* We filter the extended bit, we don't know it */
-              e.keycode = keyc; /* Store it temporarily */
+              e.keycode = KeycToOver(keyc); /* Store it temporarily */
+	      if ( keyc != e.keycode )
+                  TRACE_(key)("Keycode %02x turned back to %02x\n", keyc, e.keycode);
               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 */
               }
+              else {
+                e.keycode = KeycToOver(keyc); /* again */
+              }
 	  }
       }
 
@@ -2407,35 +3109,56 @@
 
     if (!e.keycode && virtKey != VK_NONAME)
       {
-	WARN("Unknown virtual key %X !!!\n", virtKey);
+	WARN_(key)("Unknown virtual key %X !!!\n", virtKey);
         wine_tsx11_unlock();
-	return 0;
+	return 0; /* whatever */
       }
-    else TRACE("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
+    else TRACE_(key)("Found keycode %d (0x%2X)\n",e.keycode,e.keycode);
 
     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, &status);
+    savestate = e.state;
+    if (xic) {
+          savetype = e.type;
+          if (savetype == KeyRelease) e.type = KeyPress;
+          ret = XmbLookupString(xic, &e, lpChar, sizeof(lpChar), &keysym, &status);
+          e.type = savetype;
+        }
     else
         ret = XLookupString(&e, lpChar, sizeof(lpChar), &keysym, NULL);
+    e.state = savestate;
     wine_tsx11_unlock();
 
+#ifdef XK_EuroSign
+    /* An ugly hack for EuroSign: X can't translate it to a character
+       for some locales. */
+    if (keysym == XK_EuroSign)
+    {
+        bufW[0] = 0x20AC;
+        ret = 1;
+        goto found;
+    }
+#endif
+
+#ifdef HAVE_XKB
+    if ((keysym > 0) && (keysym < 0x8000) && kbd_cp[layout_no]) {
+    /* Get lower byte */
+	lpChar[0] = keysym & 0xFF;
+	if (MultiByteToWideChar(kbd_cp[layout_no], 0, lpChar, 1, bufW, bufW_size)) {
+	    ret = 1;
+	} else {
+	    TRACE_(key)("keysym 0x%04lx treated as char 0x%02x from codepage %d, but conversion to Unicode failed\n",
+		keysym,lpChar[0],kbd_cp[layout_no]);
+	    ret = 0;
+	}
+    }
+#endif
+
     if (ret == 0)
     {
 	char dead_char;
 
-#ifdef XK_EuroSign
-        /* An ugly hack for EuroSign: X can't translate it to a character
-           for some locales. */
-        if (keysym == XK_EuroSign)
-        {
-            bufW[0] = 0x20AC;
-            ret = 1;
-            goto found;
-        }
-#endif
         /* Special case: X turns shift-tab into ISO_Left_Tab. */
         /* Here we change it back. */
         if (keysym == XK_ISO_Left_Tab)
@@ -2445,14 +3168,6 @@
             goto found;
         }
 
-	dead_char = KEYBOARD_MapDeadKeysym(keysym);
-	if (dead_char)
-        {
-	    MultiByteToWideChar(CP_UNIXCP, 0, &dead_char, 1, bufW, bufW_size);
-	    ret = -1;
-            goto found;
-        }
-
         if (keysym >= 0x01000100 && keysym <= 0x0100ffff)
         {
             /* Unicode direct mapping */
@@ -2460,25 +3175,37 @@
             ret = 1;
             goto found;
         }
-	else
-	    {
-	    const char *ksname;
 
-            wine_tsx11_lock();
-	    ksname = XKeysymToString(keysym);
-            wine_tsx11_unlock();
-	    if (!ksname)
-		ksname = "No Name";
-	    if ((keysym >> 8) != 0xff)
-		{
+	dead_char = KEYBOARD_MapDeadKeysym(keysym);
+	if (dead_char)
+	{
+#ifdef HAVE_XKB
+	    if (use_xkb && kbd_cp[layout_no])
+		MultiByteToWideChar(kbd_cp[layout_no], 0, &dead_char, 1, bufW, bufW_size);
+	    else
+#endif
+		MultiByteToWideChar(CP_UNIXCP, 0, &dead_char, 1, bufW, bufW_size);
+	    ret = -1;
+	    goto found;
+	}
+	TRACE_(key)("MapDeadKeysym failed for dead keysym 0x%08lx\n",keysym);
+	const char *ksname;
+
+        wine_tsx11_lock();
+	ksname = XKeysymToString(keysym);
+        wine_tsx11_unlock();
+	if (!ksname)
+	    ksname = "No Name";
+	if ((keysym >> 8) != 0xff)
+	    {
 		ERR("Please report: no char for keysym %04lX (%s) :\n",
                     keysym, ksname);
 		ERR("(virtKey=%X,scanCode=%X,keycode=%X,state=%X)\n",
                     virtKey, scanCode, e.keycode, e.state);
-		}
 	    }
-	}
-    else {  /* ret != 0 */
+	goto found;
+    } else {    
+    /* ret != 0 */
         /* We have a special case to handle : Shift + arrow, shift + home, ...
            X returns a char for it, but Windows doesn't. Let's eat it. */
         if (!(e.state & NumLockMask)  /* NumLock is off */
@@ -2515,18 +3242,21 @@
             ret = 0;
         }
 
-        /* Hack to detect an XLookupString hard-coded to Latin1 */
-        if (ret == 1 && keysym >= 0x00a0 && keysym <= 0x00ff && (BYTE)lpChar[0] == keysym)
-        {
-            bufW[0] = (BYTE)lpChar[0];
-            goto found;
-        }
-
-	/* perform translation to unicode */
-	if(ret)
-	{
-	    TRACE_(key)("Translating char 0x%02x to unicode\n", *(BYTE *)lpChar);
-	    ret = MultiByteToWideChar(CP_UNIXCP, 0, lpChar, ret, bufW, bufW_size);
+	if(ret) {
+        /* perform translation to unicode */
+	    {
+#ifdef HAVE_XKB
+		if (use_xkb && kbd_cp[layout_no]) {
+		    TRACE_(key)("Translating char 0x%02x from codepage %d to unicode\n", 
+			*(BYTE *)lpChar,kbd_cp[layout_no]);
+		    ret = MultiByteToWideChar(kbd_cp[layout_no], 0, lpChar, 1, bufW, bufW_size);
+		} else 
+#endif
+		{	
+		    TRACE_(key)("Translating char 0x%02x to unicode\n", *(BYTE *)lpChar);
+		    ret = MultiByteToWideChar(CP_UNIXCP, 0, lpChar, ret, bufW, bufW_size);
+		}
+	    }
 	}
     }
 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: wine-changed.tar.gz
Type: application/octet-stream
Size: 92383 bytes
Desc: not available
Url : http://www.winehq.org/pipermail/wine-devel/attachments/20061113/07cbaddd/wine-changed.tar-0001.obj
-------------- next part --------------
A non-text attachment was scrubbed...
Name: wine-xkb-add.tar.gz
Type: application/octet-stream
Size: 4673 bytes
Desc: not available
Url : http://www.winehq.org/pipermail/wine-devel/attachments/20061113/07cbaddd/wine-xkb-add.tar-0001.obj


More information about the wine-devel mailing list