Keyboard

Oleh R. Nykyforchyn nick at pu.if.ua
Fri Jan 21 02:06:18 CST 2005


Dear developers!

There are some issues with implementation of keyboard driver in Wine.
These are mainly caused by limitations of core X protocol that is used.
There no support for having several keyboard layouts at once and switching
between them on appropriate event (as Windows does). This feature is easily
implemented, for example, with XKB extension. The only thing that present
Wine x11drv can is to redetect the active layout if something is changed.
Moreover, it does it incorrectly if XKB (and several layouts) is used. 
Use of "merged" layout (e.g. English+Russian or German+French) doesn't help,
is conceptually wrong and has no perspective for future.

For example, the simplest way to have both German and French layouts is 
to construct XKB configuration with two
groups, 3 levels in each. Then, e.g., "E" key produce [e, E, 0xbf] in "French"
group and [e, E, 0x80] in "German" one (taken from layouts in keyboard.c).
By XKB rules compatibility map contains a row [e, E, e, E, 0xbf, 0x80]. x11drv
complains about not supporting >4 keysyms_per_keycode and leaves [e, E, e, E]. All
accents are ignored, closest pattern is "eE". The same with other keys. Of course
Wine will detect "United States keyboard" - no French, no German.

I propose the following structure of keyboard layouts management in Wine. First,
there is a function X11DRV_KEYBOARD_LayoutSwitched() that is called by event
handlers. If this function sees that layout we are switching to is invalid (has
not be determined yet or does not match the current state of keyboard) it
calls X11DRV_KEYBOARD_RefreshLayout(). In most cases it only changes current layout
index and there is no need in detecting layout again (as present code does).
If keyboard layout changes, we can notify appication about it (although I put this
code into #if 0 ... #endif because the respective function isn't implemented yet).

In turn, if this function detect that
the global key mapping was changed, it clears detected layout and get new
keycodes range. It also refreshes information about key redirection (if
XKB overlays are used) by call to X11DRV_KEYBOARD_RefreshOverlays(). Current
code knows nothing about overlays and therefore can't correctly determine
XKB layouts. 

We also should listen not only to XKeyEvent, but also to XkbEvents,
as changes to XKB keyboard state that actually change layout (for example, turn on
Japanese group instead of English) don't change compatibility keyboard map and
therefore are invisible to core X protocol. So it is necessary to add 
X11DRV_ProcessXkbEvent() function.

To avoid the risk of breaking existing functionality before new code is tested
on different platforms with and without XKB (which I can't do), I introduce a 
"--enable-xkb" feature to configure script that defines defines XKB_EXP in 
include/config.h. If this macro isn't set nothing changes in keyboard.c
and event.c and almost nothing in x11drv.main (one int variable is defined
and XkbQueryExtension instead of XkbUseExtension is used). If XKB_EXP is set,
but XKB was not detected in compile time, or compiled in, but absent at server
side at runtime, new code simply thinks that there is only one (first) layout and
uses core X functions for obtaining chars and keysyms (the same that old code
does). Use of new code is optional and is intended for those who need it
and suffer from restrictions of the existing code. I hope that in future 
after testing it will be possible to cut out pieces of old code completely.

Sincerely Yours
Oleh R. Nykyforchyn

--- wine-20050111/dlls/x11drv/event.c.orig	2005-01-21 08:16:10.000000000 +0200
+++ wine-20050111/dlls/x11drv/event.c	2005-01-21 08:16:10.000000000 +0200
@@ -30,6 +30,9 @@
 #ifdef HAVE_LIBXXF86DGA2
 #include <X11/extensions/xf86dga.h>
 #endif
+#if defined(XKB_EXP) && defined(HAVE_XKB)
+#include <X11/XKBlib.h>
+#endif
 
 #include <assert.h>
 #include <stdarg.h>
@@ -105,6 +108,12 @@
 extern void X11DRV_ConfigureNotify( HWND hwnd, XConfigureEvent *event );
 extern void X11DRV_MappingNotify( XMappingEvent *event );
 
+#if defined(XKB_EXP) && defined(HAVE_XKB)
+extern void X11DRV_ProcessXkbEvent( XkbEvent * event );
+extern int use_xkb;
+extern int xkbEventCode;
+#endif
+
 #ifdef HAVE_LIBXXF86DGA2
 static int DGAMotionEventType;
 static int DGAButtonPressEventType;
@@ -130,7 +139,11 @@
  */
 static int process_events( struct x11drv_thread_data *data )
 {
+#if defined(XKB_EXP) && defined(HAVE_XKB)
+    XkbEvent event;
+#else
     XEvent event;
+#endif
     int count = 0;
 
     wine_tsx11_lock();
@@ -138,10 +151,17 @@
     {
         Bool ignore;
 
+#if defined(XKB_EXP) && defined(HAVE_XKB)
+        XNextEvent( data->display, &event.core );
+        ignore = XFilterEvent( &event.core, None );
+        wine_tsx11_unlock();
+        if (!ignore) EVENT_ProcessEvent( &event.core );
+#else
         XNextEvent( data->display, &event );
         ignore = XFilterEvent( &event, None );
         wine_tsx11_unlock();
         if (!ignore) EVENT_ProcessEvent( &event );
+#endif
         count++;
         wine_tsx11_lock();
     }
@@ -267,6 +287,14 @@
   }
 #endif
 
+#if defined(XKB_EXP) && defined(HAVE_XKB)
+  if (use_xkb && (event->type == xkbEventCode)) {
+    TRACE("XkbEvent received.\n");
+    X11DRV_ProcessXkbEvent( (XkbEvent *) event );
+    return;
+  }
+#endif
+
   wine_tsx11_lock();
   if (XFindContext( display, event->xany.window, winContext, (char **)&hWnd ) != 0)
       hWnd = 0;  /* Not for a registered window */
--- wine-20050111/dlls/x11drv/x11drv_main.c.orig	2005-01-21 08:16:10.000000000 +0200
+++ wine-20050111/dlls/x11drv/x11drv_main.c	2005-01-21 08:16:10.000000000 +0200
@@ -90,6 +90,7 @@
 int usexvidmode = 0;
 int usexrandr = 1;
 int use_xkb = 1;
+int xkbEventCode = 0;
 int use_take_focus = 1;
 int managed_mode = 1;
 int client_side_with_core = 1;
@@ -483,8 +484,15 @@
 #ifdef HAVE_XKB
     if (use_xkb)
     {
-        use_xkb = XkbUseExtension( data->display, NULL, NULL );
-        if (use_xkb) 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);
+	}
     }
 #endif
 
--- wine-20050111/dlls/x11drv/keyboard.c.orig	2005-01-21 08:16:10.000000000 +0200
+++ wine-20050111/dlls/x11drv/keyboard.c	2005-01-21 08:16:10.000000000 +0200
@@ -34,6 +34,16 @@
 #include <X11/XKBlib.h>
 #endif
 
+#ifdef XKB_EXP
+#ifdef HAVE_XKB
+#define MAX_LAYOUTS 4
+#define MAX_OVERLAYS 3
+#else
+#define MAX_LAYOUTS 1
+#define MAX_OVERLAYS 1
+#endif
+#endif
+
 #include <ctype.h>
 #include <stdarg.h>
 #include <string.h>
@@ -55,8 +65,23 @@
 WINE_DECLARE_DEBUG_CHANNEL(key);
 WINE_DECLARE_DEBUG_CHANNEL(dinput);
 
+#ifdef XKB_EXP
+static int min_keycode = 0, max_keycode = 0, keysyms_per_keycode;
+static int layout_no = 0;   /* Always if we  */
+static int kbd_overlay = 0; /* don't use XKB */
+XkbDescPtr desc = NULL;
+static WORD keyc2vkey[MAX_LAYOUTS][256] = {{0}}, keyc2scan[MAX_LAYOUTS][256] = {{0}};
+static KeySym keyc2ksym[MAX_LAYOUTS][256][4] = {{{0}}};
+static KeyCode keyc2over[MAX_OVERLAYS][256] = {{0}};
+static KeyCode over2keyc[MAX_OVERLAYS][256] = {{0}};
+static unsigned kbd_layout[MAX_LAYOUTS]= {0}; /* indices into table of layouts below
+                                                for possible XKB groups */
+static unsigned kbd_valid[MAX_LAYOUTS] = {0}; /* 1 - layout is valid, 0 - otherwise */
+static unsigned global_refresh = 1;           /* rebuild all tables*/
+#else
 static int min_keycode, max_keycode, keysyms_per_keycode;
 static WORD keyc2vkey[256], keyc2scan[256];
+#endif
 
 static LPBYTE pKeyStateTable;
 static int NumLockMask, AltGrMask; /* mask in the XKeyEvent state */
@@ -840,7 +888,10 @@
 
  {0, NULL, NULL, NULL, NULL} /* sentinel */
 };
+
+#ifndef XKB_EXP
 static unsigned kbd_layout=0; /* index into above table of layouts */
+#endif
 
 /* maybe more of these scancodes should be extended? */
                 /* extended must be set for ALT_R, CTRL_R,
@@ -946,11 +997,131 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
 };
 
+#ifdef XKB_EXP
+/**********************************************************************
+ *		X11DRV_KEYBOARD_LayoutSwitched
+ *
+ *  Effective group and overlays are taken into consideration
+ */
+
+static void
+X11DRV_KEYBOARD_RefreshLayout (Display * display);
+
+
+HKL X11DRV_GetKeyboardLayout(DWORD);
+
+static Bool
+X11DRV_KEYBOARD_LayoutSwitched (void)
+{ /* enter and exit in tsx11_lock'ed state */
+  int changed = 0;
+  Display *display = thread_display();
+#ifdef HAVE_XKB
+  if (use_xkb) {
+    XkbStateRec state;
+    XkbGetState(display, XkbUseCoreKbd, &state);
+    changed = (state.locked_group != layout_no);
+    layout_no = state.locked_group;
+  }
+  else
+#endif
+  {
+    layout_no = 0;
+    changed   = 0;
+  }
+  if (!kbd_valid[layout_no]) {
+    X11DRV_KEYBOARD_RefreshLayout (display);
+    kbd_valid[layout_no] = 1;
+    changed = 1;
+  }
+#if 0
+  if (changed) {
+    HWND hwnd == GetFocus();
+    if (!hwnd) hwnd = GetActiveWindow();
+    PostMessageW(hwnd, WM_INPUTLANGCHANGEREQUEST,
+               0 /*FIXME*/, (LPARAM)X11DRV_GetKeyboardLayout(0));
+  }
+#endif
+  return changed;
+}
+
+
+void
+X11DRV_InitKeyboard( BYTE * );
+
+#endif /* XKB_EXP */
+
+/**********************************************************************
+ *              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)
 {
+#ifdef XKB_EXP
+    KeyCode kc;
+    KeySym keysym = 0;
+    WORD vkey = 0;
+    int savetype;
+    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, NULL, 0, &keysym, NULL);
+          e->type = savetype;
+        }
+    else
+        XLookupString(e, NULL, 0, &keysym, NULL);
+    e->state = savestate;
+
+#ifdef HAVE_XKB
+    int checkno = 1, checklayout = 1, i;
+#else
+    int checkno = 0, checklayout = 1, i;
+#endif
+
+  again:
+    kc = over2keyc[kbd_overlay][e->keycode];
+    TRACE_(key)("Treated as e->keycode = %x\n", kc);
+
+    if (kbd_valid[layout_no]) {
+      if ((keysym >= 0xFFAE) && (keysym <= 0xFFB9) && (keysym != 0xFFAF)
+	&& (e->state & NumLockMask))
+        /* Only the Keypad keys 0-9 and . send different keysyms
+         * depending on the NumLock state */
+        vkey = nonchar_key_vkey[keysym & 0xFF];
+      else
+        vkey = keyc2vkey[layout_no][kc];
+
+      for (i = 0; i < 4; i++) {
+          TRACE_(key)("Compare: 0x%04lx <> 0x%04lx, group %d, overlay %d, level %d\n",
+             keysym,keyc2ksym[layout_no][kc][i], layout_no, kbd_overlay, i);
+          if (keyc2ksym[layout_no][kc][i] && (keysym == keyc2ksym[layout_no][kc][i])) {
+            if (vkey) checkno = checklayout = 0;
+            break;
+          }
+      }
+    }
+
+    if (checkno || checklayout) {
+       if (checkno) checkno     = 0;
+       else  {
+          checklayout = 0;
+          kbd_valid[layout_no] = 0;
+       }
+       X11DRV_KEYBOARD_LayoutSwitched();
+       goto again;
+    }
+
+    e->keycode = kc;
+    return vkey;
+#else /* no XKB_EXP */
     KeySym keysym = 0;
 
     if (xic)
@@ -967,8 +1138,11 @@
     TRACE_(key)("e->keycode = %x\n", e->keycode);
 
     return keyc2vkey[e->keycode];
+#endif /* no XKB_EXP */
 }
 
+
+
 static BOOL NumState=FALSE, CapsState=FALSE;
 
 
@@ -1077,7 +1251,11 @@
         for (j = 0; j < 8; j++)
         {
             if (!(event->key_vector[i] & (1<<j))) continue;
+#ifdef XKB_EXP
+            switch(keyc2vkey[layout_no][(i * 8) + j] & 0xff)
+#else
             switch(keyc2vkey[(i * 8) + j] & 0xff)
+#endif
             {
             case VK_MENU:    alt = 1; break;
             case VK_CONTROL: control = 1; break;
@@ -1101,7 +1279,12 @@
     KeySym keysym = 0;
     WORD vkey = 0, bScan;
     DWORD dwFlags;
+#ifdef XKB_EXP
+    int ascii_chars, savetype;
+    unsigned int savestate;
+#else
     int ascii_chars;
+#endif
     XIC xic = X11DRV_get_ic( hwnd );
     DWORD event_time = event->time - X11DRV_server_startticks;
     Status status = 0;
@@ -1110,10 +1293,24 @@
 		event->type, event->window, event->state, event->keycode);
 
     wine_tsx11_lock();
+#ifdef XKB_EXP
+    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;
+#else
     if (xic)
         ascii_chars = XmbLookupString(xic, event, Str, sizeof(Str), &keysym, &status);
     else
         ascii_chars = XLookupString(event, Str, sizeof(Str), &keysym, NULL);
+#endif
     wine_tsx11_unlock();
 
     /* Ignore some unwanted events */
@@ -1158,7 +1355,11 @@
         wine_tsx11_unlock();
 	if (!ksname)
 	  ksname = "No Name";
+#ifdef XKB_EXP
+	TRACE_(key)("%s : keysym=%lX (%s), # of chars=%d / 0x%02x / '%c'\n",
+#else
 	TRACE_(key)("%s : keysym=%lX (%s), # of chars=%d / 0x%02x / '%s'\n",
+#endif
                     (event->type == KeyPress) ? "KeyPress" : "KeyRelease",
                     keysym, ksname, ascii_chars, Str[0] & 0xff, Str);
     }
@@ -1203,7 +1404,11 @@
 	NumState = FALSE;
 	CapsState = FALSE;
 
+#ifdef XKB_EXP
+	bScan = keyc2scan[layout_no][event->keycode] & 0xFF;
+#else
 	bScan = keyc2scan[event->keycode] & 0xFF;
+#endif
 	TRACE_(key)("bScan = 0x%02x.\n", bScan);
 
 	dwFlags = 0;
@@ -1215,6 +1420,410 @@
    }
 }
 
+#ifdef XKB_EXP
+#ifdef HAVE_XKB
+
+static const char * const xkb_event_names[] =
+{
+  "XkbNewKeyboardNotify", "XkbMapNotify", "XkbStateNotify",
+  "XkbControlsNotify", "XkbIndicatorStateNotify", "XkbIndicatorMapNotify",
+  "XkbNamesNotify", "XkbCompatMapNotify", "XkbBellNotify", "XkbActionMessage",
+  "XkbAccessXnotify", "XkbExtensionDeviceNotify"
+};
+
+/***********************************************************************
+ *           X11DRV_ProcessXkbEvent
+ *
+ * Handle a XKB event
+ */
+void X11DRV_ProcessXkbEvent( XkbEvent * event )
+{
+    int i;
+    TRACE_(key)(" : type %s\n",xkb_event_names[event->any.xkb_type]);
+    switch ( event->any.xkb_type ) {
+      case XkbMapNotify:
+        global_refresh = 1;
+      case XkbControlsNotify:
+        for (i=0; i<MAX_LAYOUTS; i++) kbd_valid[i]=0;
+      case XkbStateNotify:
+	wine_tsx11_lock();
+        X11DRV_KEYBOARD_LayoutSwitched();
+	wine_tsx11_unlock();
+	break;
+      case XkbNewKeyboardNotify:
+        X11DRV_InitKeyboard( pKeyStateTable ); break;
+      default: 
+        break;
+    }
+    
+}
+
+/**********************************************************************
+ *		X11DRV_KEYBOARD_RefreshOverlay
+ *
+ */
+
+static void
+X11DRV_KEYBOARD_RefreshOverlays(void) { /* should contain server fields */
+
+  KeyCode keyc, over;
+  int i;  
+  char betype, bedata, overlaytype;
+
+
+  for (i = 1; i <= 2; i++) {
+    if (i == 1) overlaytype = XkbKB_Overlay1;
+    else  if (i == 2) overlaytype = XkbKB_Overlay2;
+    for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++) {
+        over = keyc;
+/*        TRACE_(key)("Check keycode %d (Overlay%d)\n", keyc, i); */
+    haveoverlay:
+        betype = (desc->server->behaviors)[over].type;
+        bedata = (desc->server->behaviors)[over].data;
+        if (betype == overlaytype) {
+/*          TRACE_(key)("    changed to %d\n", bedata); */
+          over = bedata;
+          goto haveoverlay;
+        }
+        if (keyc != over) {
+          TRACE_(key)("Keycode %d changed to %d (Overlay%d)\n", keyc, over, i);
+          keyc2over[i][keyc] = over;
+        }
+    }
+
+    for (over = min_keycode; (over && (over <= max_keycode)); over++) {
+      for (keyc = min_keycode; keyc < over; keyc++) {
+        if (keyc2over[i][keyc] == keyc2over[i][over]) {
+          over2keyc[i][over] = over2keyc[i][keyc];
+          break;
+        }
+      }
+      if (over2keyc[i][over] != over)
+          TRACE_(key)(" In Overlay%d keycode %d behaves as %d\n",
+                       i, over, over2keyc[i][over]);
+    }
+  }
+}
+#endif /* HAVE_XKB */
+
+/**********************************************************************
+ *		X11DRV_KEYBOARD_CharsForKeycode
+ *
+ */
+
+static Bool
+X11DRV_KEYBOARD_CharsForKeyCode (Display *display,
+                                    KeyCode keyc,
+                                    KeySym (*ksym)[4], /*    output     */
+                                    char (*chars)[4],  /* return values */
+                                    int *numchars) {   /*   how many    */
+  char Chars[5];
+  int i, w;
+  KeySym keysym;
+  (*numchars) = 0;
+
+#ifdef HAVE_XKB
+  unsigned info;
+  int group = layout_no;
+  if (use_xkb && desc) {
+    unsigned ctrls = desc->ctrls->enabled_ctrls;
+    if       (ctrls & XkbOverlay1Mask) kbd_overlay = 1;
+    else  if (ctrls & XkbOverlay2Mask) kbd_overlay = 2;
+        else kbd_overlay = 0;
+
+    if (kbd_overlay)
+        keyc = keyc2over[kbd_overlay][keyc];
+
+    w = XkbKeyNumGroups(desc,keyc);
+    if (!w) {                                                 
+      TRACE_(key)("No groups for keycode %d\n", keyc);
+      return False;
+    }
+    info = XkbOutOfRangeGroupInfo(XkbKeyGroupInfo(desc,keyc));
+    if (group>=w)
+      switch (info) {
+        case (XkbRedirectIntoRange): group = XkbOutOfRangeGroupInfo(XkbKeyGroupInfo(desc,keyc));
+                                   if (group >= w) w = 0; break;
+        case (XkbClampIntoRange): ;  group = w -1; break;
+        default: group %= w; break;
+      }
+    w = XkbKeyGroupWidth(desc,keyc,group);
+  } else
+#endif
+  w = 4;
+
+  for (i = 0; i < w; i++) {
+#ifdef HAVE_XKB
+    if (use_xkb) keysym=XkbKeySymEntry(desc, keyc, i, group);
+    else
+#endif
+	keysym = XKeycodeToKeysym (display, keyc, i);
+    if (ksym != NULL) (*ksym)[i] = keysym;
+    if (keysym == NoSymbol) break;
+    if ((keysym < 0x8000) && (keysym != ' ')) {
+#ifdef HAVE_XKB
+          if ((!use_xkb) || (!XkbTranslateKeySym(display, &keysym, 0, &Chars[i], 1, NULL)))
+#endif
+          {
+                TRACE_(key)("XKB could not translate keysym %ld\n", keysym);
+                /* FIXME: query what keysym is used as Mode_switch, fill XKeyEvent
+                 * with appropriate ShiftMask and Mode_switch, use XLookupString
+                 * to get character in the local encoding.
+                 */
+                Chars[i] = keysym & 0xFF;
+          }
+    }
+    else Chars[i] = KEYBOARD_MapDeadKeysym(keysym);
+
+    if (Chars[i] && ((*numchars) == i)) {
+	  (*numchars)++;
+          if (chars) (*chars)[i]=Chars[i];
+	  continue;
+    }
+    Chars[i]=' ';
+    if (chars) (*chars)[i]='\0';
+  }
+  Chars[i]='\0';
+  TRACE_(key)("Keycode %d translated to %d chars: \"%s\"\n", keyc, *numchars, Chars);
+  if (ksym)  TRACE_(key)("  Keysyms: %s, %s, %s, %s\n",
+            XKeysymToString((*ksym)[0]), XKeysymToString((*ksym)[1]),
+            XKeysymToString((*ksym)[2]), XKeysymToString((*ksym)[3]));
+
+  if (*numchars) return True;
+  else return False;
+}
+
+/**********************************************************************
+ *		X11DRV_KEYBOARD_RefreshLayout
+ *
+ *  Effective group and overlays are taken into consideration
+ */
+
+static void
+X11DRV_KEYBOARD_RefreshLayout (Display * display)
+{
+/* enter in tsx11_lock()'ed state */
+
+  int min_key_code, max_key_code;
+  int current, key, i, ok, have_chars;
+  int score, match, miss, inv;
+  int prev, bestScore, bestInv, bestMiss;
+  WORD scan, vkey;
+
+  const char (*lkey)[MAIN_LEN][4];
+  static char keyc2char[256][4] = {{0}};
+  KeyCode keyc;
+  KeySym keysym;
+
+#ifdef HAVE_XKB
+  if (use_xkb) {
+    desc = XkbGetKeyboard(display,
+           XkbAllComponentsMask,
+           XkbUseCoreKbd);
+    XkbGetControls(display, XkbAllControlsMask, desc);
+    min_key_code = desc->min_key_code;
+    max_key_code = desc->max_key_code;
+    TRACE("Selected XKB group %d, keycodes from %d to %d\n",
+          layout_no, min_key_code, max_key_code);
+  } else
+#endif
+  {
+    XDisplayKeycodes(display, &min_key_code, &max_key_code);
+    TRACE("No XKB, keycodes from %d to %d\n",
+          min_key_code, max_key_code);
+  }
+
+  if ((min_keycode != min_key_code) || (max_keycode != max_key_code)) {
+    global_refresh = 1;
+    min_keycode = min_key_code; max_keycode = max_key_code;
+  }
+
+  if (global_refresh) {
+    for (i=0; i<MAX_OVERLAYS; i++) {
+      kbd_valid[i] = 0;
+      for (key=0; key<256; key++)
+        keyc2over[i][key] = over2keyc[i][key] = key;
+    }
+#ifdef HAVE_XKB
+    if (use_xkb) X11DRV_KEYBOARD_RefreshOverlays();
+#endif
+    global_refresh = 0;
+  }
+
+  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] = NoSymbol; keyc2char[key][i] ='\0';
+    }
+  }
+
+  for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+     if (!X11DRV_KEYBOARD_CharsForKeyCode (display,
+                                           keyc, &keyc2ksym[layout_no][keyc],
+                                           &keyc2char[keyc], &have_chars)) {
+#ifdef HAVE_XKB
+       TRACE_(key)("No keysyms translated in the group %d for keycode %d\n", layout_no, keyc);
+#else
+       TRACE_(key)("No keysyms translated for keycode %d\n", keyc);
+#endif
+     }
+
+#ifdef HAVE_XKB
+  if (use_xkb && desc) {
+    XkbFreeKeyboard(desc, XkbAllComponentsMask,True);
+    desc = NULL;
+  }
+#endif
+
+  wine_tsx11_unlock();
+   
+  bestScore = -1; bestInv = max_keycode; bestMiss = MAIN_LEN; prev = 0;
+  for (current = 0; main_key_tab[current].comment; current++) {
+       score = match = miss = inv = 0;
+       lkey = main_key_tab[current].key;
+	/* search for a match in layout table */
+	/* right now, we just find an absolute match for defined positions */
+	/* (undefined positions are ignored, so if it's defined as "3#" in */
+	/* the table, it's okay that the X server has "3#ё", for example) */
+	/* however, the score will be higher for longer matches */
+       for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++) {
+         if (!keyc2char[keyc][0]) continue;
+	 for (key = 0; key < MAIN_LEN; key++) {
+	  for (ok = i = 0; ((ok >= 0) && ((*lkey)[key][i])); i++) {
+	    if ((*lkey)[key][i] == keyc2char[keyc][i])  ok++;
+	    else ok = -1;
+	  }
+	  if (ok > 0) {
+	    score += ok;
+	    break;
+	  }
+	 }
+	/* count the matches and mismatches */
+	 if (ok > 0) {
+	   match++;
+/*           TRACE_(key)("keycode %d matches entry %d\n",
+                      keyc, key); */
+	  /* and how much the keycode order matches */
+	   if (key < prev) inv++;
+	   prev = key;
+	 }
+         else {
+          /* print spaces instead of \0's */
+/*           TRACE_(key)("mismatch for keycode %d\n",
+                      keyc); */
+	   miss++;
+           for (i=0; i<4; i++) {
+            if (keyc2char[keyc][i]) score--;
+            else break;
+           }
+	 }
+      }
+      TRACE("\"%s\": matches=%d, mismatches=%d, inversions=%d, score=%d\n",
+           main_key_tab[current].comment,
+           match, miss, inv, score);
+      if ((score > bestScore) ||
+         ((score > bestScore) && (inv<bestInv))) {
+           bestScore  = score;
+           bestInv    = inv;
+           bestMiss   = miss;
+           kbd_layout[layout_no] = current;
+      }
+  }
+
+  if (bestMiss) {
+    TRACE("Detected layout is \"%s\" (closest match)\n",
+                     main_key_tab[kbd_layout[layout_no]].comment);
+  }
+  else {
+    TRACE("Detected layout is \"%s\" (exact match)\n",
+                     main_key_tab[kbd_layout[layout_no]].comment);
+  }
+
+  for (keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+  {
+        keysym = keyc2ksym[layout_no][keyc][0];
+        have_chars = (keyc2char[keyc][0]);
+        vkey = 0; scan = 0;
+        if (keysym)  /* otherwise, keycode not used */
+        {
+            if ((keysym >> 8) == 0xFF)         /* non-character key */
+            {
+                vkey = nonchar_key_vkey[keysym & 0xff];
+                scan = nonchar_key_scan[keysym & 0xff];
+		/* set extended bit when necessary */
+		if (scan & 0x100) vkey |= 0x100;
+            } else if (keysym == 0x20) {                 /* Spacebar */
+	        vkey = VK_SPACE;
+		scan = 0x39;
+	    } else if (have_chars) {
+	      /* we seem to need to search the layout-dependent scancodes */
+	      lkey = main_key_tab[kbd_layout[layout_no]].key;
+	      int maxlen=0,maxval=-1;
+	      /* find key with longest match streak */
+	      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[keyc][i]) {
+                   ok=0; break;
+                  }
+                }
+		if (ok||(i>maxlen)) {
+		  maxlen=i; maxval=key;
+		}
+		if (ok) break;
+	      }
+	      if (maxval>=0) {
+		/* got it */
+		const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout[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[keyc][0];
+                out[1] = (unsigned char)keyc2char[keyc][1];
+                out[2] = (unsigned char)keyc2char[keyc][2];
+                out[3] = (unsigned char)keyc2char[keyc][3];
+                strncpy(&bkey[0], keyc2char[keyc], 4); bkey[5] = '\0';
+                TRACE_(key)("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 ...
+       * Take from xxxInitKeyboard if You need it
+       */
+#endif /* 0 */
+        }
+        TRACE("keycode %04x => vkey %04x\n", keyc, vkey);
+        keyc2vkey[layout_no][keyc] = vkey;
+        keyc2scan[layout_no][keyc] = scan;
+  } /* for */
+
+    wine_tsx11_lock();
+
+    /* If some keys still lack scancodes, assign some arbitrary ones to them now */
+  for (scan = 0x60, keyc = min_keycode; (keyc && (keyc <= max_keycode)); keyc++)
+      if (keyc2vkey[layout_no][keyc]&&!keyc2scan[layout_no][keyc]) {
+	char *ksname;
+	keysym = XKeycodeToKeysym(display, keyc, 0);
+	ksname = XKeysymToString(keysym);
+	if (!ksname) ksname = "NoSymbol";
+
+	/* should make sure the scancode is unassigned here, but >=0x60 currently always is */
+
+	TRACE_(key)("assigning scancode %02x to unidentified keycode %02x (%s)\n",scan,keyc,ksname);
+	keyc2scan[layout_no][keyc]=scan++;
+      }
+
+/* exit in tsx11_lock()'ed state again */
+}
+
+#else /* no XKB_EXP */
+
 /**********************************************************************
  *		X11DRV_KEYBOARD_DetectLayout
  *
@@ -1325,12 +1934,26 @@
   TRACE("detected layout is \"%s\"\n", main_key_tab[kbd_layout].comment);
 }
 
+#endif /* no XKB_EXP */
+
 /**********************************************************************
  *		InitKeyboard (X11DRV.@)
  */
 void X11DRV_InitKeyboard( BYTE *key_state_table )
 {
     Display *display = thread_display();
+#ifdef XKB_EXP
+    XModifierKeymap *mmp;
+    KeyCode * kcp;
+    int i;
+
+    pKeyStateTable = key_state_table;
+
+    wine_tsx11_lock();
+    
+    global_refresh = 1;
+    X11DRV_KEYBOARD_LayoutSwitched();
+#else
     KeySym *ksp;
     XModifierKeymap *mmp;
     KeySym keysym;
@@ -1350,6 +1973,7 @@
     /* We are only interested in keysyms_per_keycode.
        There is no need to hold a local copy of the keysyms table */
     XFree(ksp);
+#endif
 
     mmp = XGetModifierMapping(display);
     kcp = mmp->modifiermap;
@@ -1372,6 +1996,7 @@
     }
     XFreeModifiermap(mmp);
 
+#ifndef XKB_EXP
     /* Detect the keyboard layout */
     X11DRV_KEYBOARD_DetectLayout();
     lkey = main_key_tab[kbd_layout].key;
@@ -1527,6 +2152,7 @@
 	TRACE_(key)("assigning scancode %02x to unidentified keycode %02x (%s)\n",scan,keyc,ksname);
 	keyc2scan[keyc]=scan++;
       }
+#endif /* no XKB_EXP */
 
     /* Now store one keycode for each modifier. Used to simulate keypresses. */
     kcControl = XKeysymToKeycode(display, XK_Control_L);
@@ -1587,7 +2213,11 @@
         FIXME("couldn't return keyboard layout for thread %04lx\n", dwThreadid);
 
 #if 0
+#ifdef XKB_EXP
+    layout = main_key_tab[kbd_layout[layout_no]].lcid;
+#else
     layout = main_key_tab[kbd_layout].lcid;
+#endif
 #else
     /* FIXME:
      * Winword uses return value of GetKeyboardLayout as a codepage
@@ -1623,7 +2253,11 @@
     DWORD layout;
     LANGID langid;
 
+#ifdef XKB_EXP
+    layout = main_key_tab[kbd_layout[layout_no]].lcid;
+#else
     layout = main_key_tab[kbd_layout].lcid;
+#endif
     /* see comment for GetKeyboardLayout */
     langid = PRIMARYLANGID(LANGIDFROMLCID(layout));
     if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
@@ -1728,7 +2362,11 @@
             cChar, keysym, keysym, keycode, keycode);
 
     /* keycode -> (keyc2vkey) vkey */
+#ifdef XKB_EXP
+    ret = keyc2vkey[layout_no][keycode];
+#else
     ret = keyc2vkey[keycode];
+#endif
 
     if (!keycode || !ret)
     {
@@ -1737,6 +2375,15 @@
     }
 
     index = -1;
+#ifdef XKB_EXP
+    for (i = 0; i < 4; i++) /* find shift state */
+    {
+      if (keyc2ksym[layout_no][keycode][i] && (keyc2ksym[layout_no][keycode][i] == keysym)) {
+         index = i;
+         break;
+      }
+    }
+#else
     wine_tsx11_lock();
     for (i = 0; i < 4; i++) /* find shift state */
     {
@@ -1747,6 +2394,7 @@
         }
     }
     wine_tsx11_unlock();
+#endif
 
     switch (index)
     {
@@ -1790,8 +2438,13 @@
 			/* let's do vkey -> keycode -> scan */
 			int keyc;
 			for (keyc=min_keycode; keyc<=max_keycode; keyc++)
+#ifdef XKB_EXP
+				if ((keyc2vkey[layout_no][keyc] & 0xFF) == wCode)
+					returnMVK (keyc2scan[layout_no][keyc] & 0xFF);
+#else
 				if ((keyc2vkey[keyc] & 0xFF) == wCode)
 					returnMVK (keyc2scan[keyc] & 0xFF);
+#endif
 			TRACE("returning no scan-code.\n");
 		        return 0; }
 
@@ -1799,8 +2452,13 @@
 			/* let's do scan -> keycode -> vkey */
 			int keyc;
 			for (keyc=min_keycode; keyc<=max_keycode; keyc++)
+#ifdef XKB_EXP
+				if ((keyc2scan[layout_no][keyc] & 0xFF) == (wCode & 0xFF))
+					returnMVK (keyc2vkey[layout_no][keyc] & 0xFF);
+#else
 				if ((keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
 					returnMVK (keyc2vkey[keyc] & 0xFF);
+#endif
 			TRACE("returning no vkey-code.\n");
 		        return 0; }
 
@@ -1828,6 +2486,21 @@
 			/* We exit on the first keycode found, to speed up the thing. */
 			for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
 			{ /* Find a keycode that could have generated this virtual key */
+#ifdef XKB_EXP
+			    if  ((keyc2vkey[layout_no][keyc] & 0xFF) == wCode)
+			    { /* We filter the extended bit, we don't know it */
+                                e.keycode = keyc2over[kbd_overlay][keyc]; /* Store it temporarily */
+                                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 = keyc2over[kbd_overlay][keyc]; /* again */
+                                }
+			    }
+#else
 			    if  ((keyc2vkey[keyc] & 0xFF) == wCode)
 			    { /* We filter the extended bit, we don't know it */
 			        e.keycode = keyc; /* Store it temporarily */
@@ -1836,6 +2509,7 @@
 					 state), so set it to 0, we'll find another one */
 				}
 			    }
+#endif
 			}
 
 			if ((wCode>=VK_NUMPAD0) && (wCode<=VK_NUMPAD9))
@@ -1947,7 +2621,11 @@
   /* let's do scancode -> keycode -> keysym -> String */
 
   for (keyi=min_keycode; keyi<=max_keycode; keyi++)
+#ifdef XKB_EXP
+      if ((keyc2scan[layout_no][keyi]) == scanCode)
+#else
       if ((keyc2scan[keyi]) == scanCode)
+#endif
          break;
   if (keyi <= max_keycode)
   {
@@ -2077,7 +2755,12 @@
     XKeyEvent e;
     KeySym keysym = 0;
     INT ret;
+#ifdef XKB_EXP
+    int keyc, savetype;
+    unsigned int savestate;
+#else
     int keyc;
+#endif
     char lpChar[10];
     HWND focus;
     XIC xic;
@@ -2139,6 +2822,21 @@
     /* We exit on the first keycode found, to speed up the thing. */
     for (keyc=min_keycode; (keyc<=max_keycode) && (!e.keycode) ; keyc++)
       { /* Find a keycode that could have generated this virtual key */
+#ifdef XKB_EXP
+          if  ((keyc2vkey[layout_no][keyc] & 0xFF) == virtKey)
+          { /* We filter the extended bit, we don't know it */
+              e.keycode = keyc2over[kbd_overlay][keyc]; /* Store it temporarily */
+              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 = keyc2over[kbd_overlay][keyc]; /* again */
+              }
+	  }
+#else
           if  ((keyc2vkey[keyc] & 0xFF) == virtKey)
           { /* We filter the extended bit, we don't know it */
               e.keycode = keyc; /* Store it temporarily */
@@ -2147,6 +2845,7 @@
                          state), so set it to 0, we'll find another one */
               }
 	  }
+#endif
       }
 
     if ((virtKey>=VK_NUMPAD0) && (virtKey<=VK_NUMPAD9))
@@ -2166,10 +2865,23 @@
     TRACE_(key)("type %d, window %lx, state 0x%04x, keycode 0x%04x\n",
 		e.type, e.window, e.state, e.keycode);
 
+#ifdef XKB_EXP
+    savestate = e.state;
+    if (xic) {
+          savetype = e.type;
+          if (savetype == KeyRelease) e.type = KeyPress;
+          ret = XmbLookupString(xic, &e, lpChar, sizeof(lpChar), &keysym, NULL);
+          e.type = savetype;
+        }
+    else
+        ret = XLookupString(&e, lpChar, sizeof(lpChar), &keysym, NULL);
+    e.state = savestate;
+#else
     if (xic)
         ret = XmbLookupString(xic, &e, lpChar, sizeof(lpChar), &keysym, NULL);
     else
         ret = XLookupString(&e, lpChar, sizeof(lpChar), &keysym, NULL);
+#endif
     wine_tsx11_unlock();
 
     if (ret == 0)
--- wine-20050111/configure.ac.orig	2005-01-21 08:16:10.000000000 +0200
+++ wine-20050111/configure.ac	2005-01-21 08:16:10.000000000 +0200
@@ -16,6 +16,7 @@
 AC_ARG_ENABLE(debug, AC_HELP_STRING([--disable-debug],[compile out all debugging messages]))
 AC_ARG_ENABLE(trace, AC_HELP_STRING([--disable-trace],[compile out TRACE messages]))
 AC_ARG_ENABLE(win64, AC_HELP_STRING([--enable-win64], [build a Win64 emulator on AMD64 (won't run Win32 binaries)]))
+AC_ARG_ENABLE(xkb,   AC_HELP_STRING([--enable-xkb],   [enable XKB support (EXPERIMENTAL)]))
 
 AC_ARG_WITH(opengl,    AC_HELP_STRING([--without-opengl],[do not use OpenGL]))
 AC_ARG_WITH(curses,    AC_HELP_STRING([--without-curses],[do not use curses]))
@@ -39,6 +40,11 @@
   DLLDEFS="$DLLDEFS -DWINE_NO_TRACE_MSGS"
 fi
 
+if test "$enable_xkb" = "yes"
+then
+  AC_DEFINE(XKB_EXP)
+fi
+
 dnl **** Check for some programs ****
 
 AC_CANONICAL_HOST
--- wine-20050111/configure.orig	2005-01-21 08:16:10.000000000 +0200
+++ wine-20050111/configure	2005-01-21 08:16:10.000000000 +0200
@@ -865,6 +865,7 @@
   --disable-trace         compile out TRACE messages
   --enable-win64          build a Win64 emulator on AMD64 (won't run Win32
                           binaries)
+  --enable-xkb            enable XKB support (EXPERIMENTAL)
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
@@ -1384,6 +1385,11 @@
   enableval="$enable_win64"
 
 fi;
+# Check whether --enable-xkb or --disable-xkb was given.
+if test "${enable_xkb+set}" = set; then
+  enableval="$enable_xkb"
+
+fi;
 
 
 # Check whether --with-opengl or --without-opengl was given.
@@ -1425,6 +1431,14 @@
   DLLDEFS="$DLLDEFS -DWINE_NO_TRACE_MSGS"
 fi
 
+if test "$enable_xkb" = "yes"
+then
+  cat >>confdefs.h <<\_ACEOF
+#define XKB_EXP 1
+_ACEOF
+
+fi
+
 
 # Make sure we can run config.sub.
 $ac_config_sub sun4 >/dev/null 2>&1 ||
--- wine-20050111/include/config.h.in.orig	2005-01-21 08:16:10.000000000 +0200
+++ wine-20050111/include/config.h.in	2005-01-21 08:16:10.000000000 +0200
@@ -785,6 +785,9 @@
 /* Define if you have the XKB extension */
 #undef HAVE_XKB
 
+/* Define if you want to USE XKB extension */
+#undef XKB_EXP
+
 /* Define if Xrender has the XRenderSetPictureTransform function */
 #undef HAVE_XRENDERSETPICTURETRANSFORM
 



More information about the wine-patches mailing list