src/gui/kernel/qkeymapper_x11.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/kernel/qkeymapper_x11.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1678 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qkeymapper_p.h"
+
+#include "qdebug.h"
+#include "qtextcodec.h"
+#include "qwidget.h"
+
+#include "qapplication_p.h"
+#include "qevent_p.h"
+#include "qt_x11_p.h"
+
+#ifndef QT_NO_XKB
+#  include <X11/XKBlib.h>
+#endif
+
+#define XK_MISCELLANY
+#define XK_LATIN1
+#define XK_KOREAN
+#define XK_XKB_KEYS
+#include <X11/keysymdef.h>
+
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_XKB
+
+// bring in the auto-generated xkbLayoutData
+#include "qkeymapper_x11_p.cpp"
+
+#ifdef QT_LINUXBASE
+// LSB's IsKeypadKey define is wrong - see
+// http://bugs.linuxbase.org/show_bug.cgi?id=2521
+#undef IsKeypadKey
+#define IsKeypadKey(keysym) \
+      (((KeySym)(keysym) >= XK_KP_Space) && ((KeySym)(keysym) <= XK_KP_Equal))
+
+#undef IsPrivateKeypadKey
+#define IsPrivateKeypadKey(keysym) \
+      (((KeySym)(keysym) >= 0x11000000) && ((KeySym)(keysym) <= 0x1100FFFF))
+#endif
+
+static void getLocaleAndDirection(QLocale *locale,
+                                  Qt::LayoutDirection *direction,
+                                  const QByteArray &layoutName,
+                                  const QByteArray &variantName)
+{
+    int i = 0;
+    while (xkbLayoutData[i].layout != 0) {
+        if (layoutName == xkbLayoutData[i].layout && variantName == xkbLayoutData[i].variant) {
+            *locale = QLocale(xkbLayoutData[i].language, xkbLayoutData[i].country);
+            *direction = xkbLayoutData[i].direction;
+            return;
+        }
+        ++i;
+    }
+    *locale = QLocale::c();
+    *direction = Qt::LeftToRight;
+}
+#endif // QT_NO_XKB
+
+
+// from qapplication_x11.cpp
+extern uchar qt_alt_mask;
+extern uchar qt_meta_mask;
+extern uchar qt_super_mask;
+extern uchar qt_hyper_mask;
+extern uchar qt_mode_switch_mask;
+uchar qt_num_lock_mask = 0;
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+// ### we should really resolve conflicts with other masks by
+// ### decomposing the Qt::KeyboardModifers in possibleKeys()
+#define SETMASK(sym, mask)                                              \
+    do {                                                                \
+        if (qt_alt_mask == 0                                            \
+            && qt_meta_mask != mask                                     \
+            && qt_super_mask != mask                                    \
+            && qt_hyper_mask != mask                                    \
+            && (sym == XK_Alt_L || sym == XK_Alt_R)) {                  \
+            qt_alt_mask = mask;                                         \
+        }                                                               \
+        if (qt_meta_mask == 0                                           \
+            && qt_alt_mask != mask                                      \
+            && qt_super_mask != mask                                    \
+            && qt_hyper_mask != mask                                    \
+            && (sym == XK_Meta_L || sym == XK_Meta_R)) {                \
+            qt_meta_mask = mask;                                        \
+        }                                                               \
+        if (qt_super_mask == 0                                          \
+            && qt_alt_mask != mask                                      \
+            && qt_meta_mask != mask                                     \
+            && qt_hyper_mask != mask                                    \
+            && (sym == XK_Super_L || sym == XK_Super_R)) {              \
+            qt_super_mask = mask;                                       \
+        }                                                               \
+        if (qt_hyper_mask == 0                                          \
+            && qt_alt_mask != mask                                      \
+            && qt_meta_mask != mask                                     \
+            && qt_super_mask != mask                                    \
+            && (sym == XK_Hyper_L || sym == XK_Hyper_R)) {              \
+            qt_hyper_mask = mask;                                       \
+        }                                                               \
+        if (qt_mode_switch_mask == 0                                    \
+            && qt_alt_mask != mask                                      \
+            && qt_meta_mask != mask                                     \
+            && qt_super_mask != mask                                    \
+            && qt_hyper_mask != mask                                    \
+            && sym == XK_Mode_switch) {                                 \
+            qt_mode_switch_mask = mask;                                 \
+        }                                                               \
+        if (qt_num_lock_mask == 0                                       \
+            && sym == XK_Num_Lock) {                                    \
+            qt_num_lock_mask = mask;                                    \
+        }                                                               \
+    } while(false)
+
+// qt_XTranslateKey() is based on _XTranslateKey() taken from:
+
+/* $Xorg: KeyBind.c,v 1.4 2001/02/09 02:03:34 xorgcvs Exp $ */
+
+/*
+
+Copyright 1985, 1987, 1998  The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+*/
+static int
+qt_XTranslateKey(register QXCoreDesc *dpy,
+                 KeyCode keycode,
+                 register unsigned int modifiers,
+                 unsigned int *modifiers_return,
+                 KeySym *keysym_return)
+{
+    int per;
+    register KeySym *syms;
+    KeySym sym, lsym, usym;
+
+    if (! dpy->keysyms)
+	return 0;
+    *modifiers_return = ((ShiftMask|LockMask)
+			 | dpy->mode_switch | dpy->num_lock);
+    if (((int)keycode < dpy->min_keycode) || ((int)keycode > dpy->max_keycode))
+    {
+	*keysym_return = NoSymbol;
+	return 1;
+    }
+    per = dpy->keysyms_per_keycode;
+    syms = &dpy->keysyms[(keycode - dpy->min_keycode) * per];
+    while ((per > 2) && (syms[per - 1] == NoSymbol))
+	per--;
+    if ((per > 2) && (modifiers & dpy->mode_switch)) {
+	syms += 2;
+	per -= 2;
+    }
+    if ((modifiers & dpy->num_lock) &&
+	(per > 1 && (IsKeypadKey(syms[1]) || IsPrivateKeypadKey(syms[1])))) {
+	if ((modifiers & ShiftMask) ||
+	    ((modifiers & LockMask) && (dpy->lock_meaning == XK_Shift_Lock)))
+	    *keysym_return = syms[0];
+	else
+	    *keysym_return = syms[1];
+    } else if (!(modifiers & ShiftMask) &&
+	(!(modifiers & LockMask) || (dpy->lock_meaning == NoSymbol))) {
+	if ((per == 1) || (syms[1] == NoSymbol))
+	    XConvertCase(syms[0], keysym_return, &usym);
+	else
+	    *keysym_return = syms[0];
+    } else if (!(modifiers & LockMask) ||
+	       (dpy->lock_meaning != XK_Caps_Lock)) {
+	if ((per == 1) || ((usym = syms[1]) == NoSymbol))
+	    XConvertCase(syms[0], &lsym, &usym);
+	*keysym_return = usym;
+    } else {
+	if ((per == 1) || ((sym = syms[1]) == NoSymbol))
+	    sym = syms[0];
+	XConvertCase(sym, &lsym, &usym);
+	if (!(modifiers & ShiftMask) && (sym != syms[0]) &&
+	    ((sym != usym) || (lsym == usym)))
+	    XConvertCase(syms[0], &lsym, &usym);
+	*keysym_return = usym;
+    }
+    if (*keysym_return == XK_VoidSymbol)
+	*keysym_return = NoSymbol;
+    return 1;
+}
+
+
+
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+    : keyboardInputDirection(Qt::LeftToRight), useXKB(false)
+{
+    memset(&coreDesc, 0, sizeof(coreDesc));
+
+#ifndef QT_NO_XKB
+    int opcode = -1;
+    int xkbEventBase = -1;
+    int xkbErrorBase = -1;
+    int xkblibMajor = XkbMajorVersion;
+    int xkblibMinor = XkbMinorVersion;
+    if (XkbQueryExtension(X11->display, &opcode, &xkbEventBase, &xkbErrorBase, &xkblibMajor, &xkblibMinor))
+        useXKB = true;
+#endif
+
+#if 0
+    qDebug() << "useXKB =" << useXKB;
+#endif
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+    if (coreDesc.keysyms)
+        XFree(coreDesc.keysyms);
+}
+
+QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *event)
+{
+#ifndef QT_NO_XKB
+    if (useXKB)
+        return possibleKeysXKB(event);
+#endif
+    return possibleKeysCore(event);
+}
+
+enum { MaxBits = sizeof(uint) * 8 };
+static QString translateKeySym(KeySym keysym, uint xmodifiers,
+                               int &code, Qt::KeyboardModifiers &modifiers,
+                               QByteArray &chars, int &count);
+
+QList<int> QKeyMapperPrivate::possibleKeysXKB(QKeyEvent *event)
+{
+#ifndef QT_NO_XKB
+    const int xkeycode = event->nativeScanCode();
+    const uint xmodifiers = event->nativeModifiers();
+
+    // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must
+    // always use them when determining the baseKeySym)
+    KeySym baseKeySym;
+    uint consumedModifiers;
+    if (!XkbLookupKeySym(X11->display, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)),
+                         &consumedModifiers, &baseKeySym))
+        return QList<int>();
+
+    QList<int> result;
+
+    // translate sym -> code
+    Qt::KeyboardModifiers baseModifiers = 0;
+    int baseCode = -1;
+    QByteArray chars;
+    int count = 0;
+    QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count);
+    if (baseCode == -1) {
+        if (text.isEmpty())
+            return QList<int>();
+        baseCode = text.at(0).unicode();
+    }
+
+    if (baseCode && baseCode < 0xfffe)
+        baseCode = QChar(baseCode).toUpper().unicode();
+    result += (baseCode | baseModifiers);
+
+    int pos1Bits[MaxBits];
+    int num1Bits = 0;
+
+    for (int i = 0; i < MaxBits; ++i) {
+        if (consumedModifiers & (1 << i))
+            pos1Bits[num1Bits++] = i;
+    }
+
+    const int numPerms = (1 << num1Bits);
+
+    // translate the key again using each permutation of consumedModifiers
+    for (int i = 1; i < numPerms; ++i) {
+        uint val = 0;
+        for (int j = 0; j < num1Bits; ++j) {
+            if (i & (1 << j))
+                val |= (1 << pos1Bits[j]);
+        }
+
+        if ((xmodifiers & val) != val)
+            continue;
+
+        KeySym sym;
+        uint mods;
+        if (!XkbLookupKeySym(X11->display, xkeycode, val, &mods, &sym))
+            continue;
+
+        // translate sym -> code
+        Qt::KeyboardModifiers modifiers = 0;
+        int code = -1;
+        chars.clear();
+        count = 0;
+        // mask out the modifiers needed to translate keycode
+        text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count);
+        if (code == -1) {
+            if (text.isEmpty())
+                continue;
+            code = text.at(0).unicode();
+        }
+
+        if (code && code < 0xfffe)
+            code = QChar(code).toUpper().unicode();
+        if (code == baseCode)
+            continue;
+
+        result += (code | modifiers);
+    }
+
+#if 0
+    qDebug() << "possibleKeysXKB()" << hex << result;
+#endif
+    return result;
+#else
+    Q_UNUSED(event);
+    return QList<int>();
+#endif // QT_NO_XKB
+}
+
+QList<int> QKeyMapperPrivate::possibleKeysCore(QKeyEvent *event)
+{
+    const int xkeycode = event->nativeScanCode();
+    const uint xmodifiers = event->nativeModifiers();
+
+    // first, translate key only using lock modifiers (there are no Qt equivalents for these, so we must
+    // always use them when determining the baseKeySym)
+    KeySym baseKeySym;
+    uint consumedModifiers;
+    if (!qt_XTranslateKey(&coreDesc, xkeycode, (xmodifiers & (LockMask | qt_num_lock_mask)),
+                          &consumedModifiers, &baseKeySym))
+        return QList<int>();
+
+    QList<int> result;
+
+    // translate sym -> code
+    Qt::KeyboardModifiers baseModifiers = 0;
+    int baseCode = -1;
+    QByteArray chars;
+    int count = 0;
+    QString text = translateKeySym(baseKeySym, xmodifiers, baseCode, baseModifiers, chars, count);
+    if (baseCode == -1) {
+        if (text.isEmpty())
+            return QList<int>();
+        baseCode = text.at(0).unicode();
+    }
+
+    if (baseCode && baseCode < 0xfffe)
+        baseCode = QChar(baseCode).toUpper().unicode();
+    result += (baseCode | baseModifiers);
+
+    int pos1Bits[MaxBits];
+    int num1Bits = 0;
+
+    for (int i = 0; i < MaxBits; ++i) {
+        if (consumedModifiers & (1 << i))
+            pos1Bits[num1Bits++] = i;
+    }
+
+    const int numPerms = (1 << num1Bits);
+
+    // translate the key again using each permutation of consumedModifiers
+    for (int i = 1; i < numPerms; ++i) {
+        uint val = 0;
+        for (int j = 0; j < num1Bits; ++j) {
+            if (i & (1 << j))
+                val |= (1 << pos1Bits[j]);
+        }
+
+        if ((xmodifiers & val) != val)
+            continue;
+
+        KeySym sym;
+        uint mods;
+        if (!qt_XTranslateKey(&coreDesc, xkeycode, val, &mods, &sym))
+            continue;
+
+        // translate sym -> code
+        Qt::KeyboardModifiers modifiers = 0;
+        int code = -1;
+        chars.clear();
+        count = 0;
+        // mask out the modifiers needed to translate keycode
+        text = translateKeySym(sym, xmodifiers & ~val, code, modifiers, chars, count);
+        if (code == -1) {
+            if (text.isEmpty())
+                continue;
+            code = text.at(0).unicode();
+        }
+
+        if (code && code < 0xfffe)
+            code = QChar(code).toUpper().unicode();
+        if (code == baseCode)
+            continue;
+
+        result += (code | modifiers);
+    }
+
+#if 0
+    qDebug() << "possibleKeysCore()" << hex << result;
+#endif
+    return result;
+}
+
+// for parsing the _XKB_RULES_NAMES property
+enum {
+    RulesFileIndex = 0,
+    ModelIndex = 1,
+    LayoutIndex = 2,
+    VariantIndex = 3,
+    OptionsIndex = 4
+};
+
+void QKeyMapperPrivate::clearMappings()
+{
+#ifndef QT_NO_XKB
+    if (useXKB) {
+        // try to determine the layout name and input direction by reading the _XKB_RULES_NAMES property off
+        // the root window
+        QByteArray layoutName;
+        QByteArray variantName;
+
+        Atom type = XNone;
+        int format = 0;
+        ulong nitems = 0;
+        ulong bytesAfter = 0;
+        uchar *data = 0;
+        if (XGetWindowProperty(X11->display, RootWindow(X11->display, 0), ATOM(_XKB_RULES_NAMES), 0, 1024,
+                               false, XA_STRING, &type, &format, &nitems, &bytesAfter, &data) == Success
+            && type == XA_STRING && format == 8 && nitems > 2) {
+            /*
+              index 0 == rules file name
+              index 1 == model name
+              index 2 == layout name
+              index 3 == variant name
+              index 4 == options
+            */
+            char *names[5] = { 0, 0, 0, 0, 0 };
+            char *p = reinterpret_cast<char *>(data), *end = p + nitems;
+            int i = 0;
+            do {
+                names[i++] = p;
+                p += qstrlen(p) + 1;
+            } while (p < end);
+
+            layoutName = QByteArray::fromRawData(names[2], qstrlen(names[2]));
+            variantName = QByteArray::fromRawData(names[3], qstrlen(names[3]));
+        }
+
+        // ### ???
+        // if (keyboardLayoutName.isEmpty())
+        //     qWarning("Qt: unable to determine keyboard layout, please talk to qt-bugs@trolltech.com"); ?
+
+        getLocaleAndDirection(&keyboardInputLocale,
+                              &keyboardInputDirection,
+                              layoutName,
+                              variantName);
+
+#if 0
+        qDebug() << "keyboard input locale ="
+                 << keyboardInputLocale.name()
+                 << "direction ="
+                 << keyboardInputDirection;
+#endif
+
+        if (data)
+            XFree(data);
+    } else
+#endif // QT_NO_XKB
+        {
+            if (coreDesc.keysyms)
+                XFree(coreDesc.keysyms);
+
+            coreDesc.min_keycode = 8;
+            coreDesc.max_keycode = 255;
+            XDisplayKeycodes(X11->display, &coreDesc.min_keycode, &coreDesc.max_keycode);
+
+            coreDesc.keysyms_per_keycode = 0;
+            coreDesc.keysyms = XGetKeyboardMapping(X11->display,
+                                                   coreDesc.min_keycode,
+                                                   coreDesc.max_keycode - coreDesc.min_keycode + 1,
+                                                   &coreDesc.keysyms_per_keycode);
+
+#if 0
+            qDebug() << "min_keycode =" << coreDesc.min_keycode;
+            qDebug() << "max_keycode =" << coreDesc.max_keycode;
+            qDebug() << "keysyms_per_keycode =" << coreDesc.keysyms_per_keycode;
+            qDebug() << "keysyms =" << coreDesc.keysyms;
+#endif
+
+            // ### cannot get/guess the locale with the core protocol
+            keyboardInputLocale = QLocale::c();
+            // ### could examine group 0 for RTL keys
+            keyboardInputDirection = Qt::LeftToRight;
+        }
+
+    qt_alt_mask = 0;
+    qt_meta_mask = 0;
+    qt_super_mask = 0;
+    qt_hyper_mask = 0;
+    qt_mode_switch_mask = 0;
+
+    // look at the modifier mapping, and get the correct masks for alt, meta, super, hyper, and mode_switch
+#ifndef QT_NO_XKB
+    if (useXKB) {
+        XkbDescPtr xkbDesc = XkbGetMap(X11->display, XkbAllClientInfoMask, XkbUseCoreKbd);
+        for (int i = xkbDesc->min_key_code; i < xkbDesc->max_key_code; ++i) {
+            const uint mask = xkbDesc->map->modmap ? xkbDesc->map->modmap[i] : 0;
+            if (mask == 0) {
+                // key is not bound to a modifier
+                continue;
+            }
+
+            for (int j = 0; j < XkbKeyGroupsWidth(xkbDesc, i); ++j) {
+                KeySym keySym = XkbKeySym(xkbDesc, i, j);
+                if (keySym == NoSymbol)
+                    continue;
+                SETMASK(keySym, mask);
+            }
+        }
+        XkbFreeKeyboard(xkbDesc, XkbAllComponentsMask, true);
+    } else
+#endif // QT_NO_XKB
+        {
+            coreDesc.lock_meaning = NoSymbol;
+
+            XModifierKeymap *map = XGetModifierMapping(X11->display);
+
+            if (map) {
+                int i, maskIndex = 0, mapIndex = 0;
+                for (maskIndex = 0; maskIndex < 8; maskIndex++) {
+                    for (i = 0; i < map->max_keypermod; i++) {
+                        if (map->modifiermap[mapIndex]) {
+                            KeySym sym;
+                            int x = 0;
+                            do {
+                                sym = XKeycodeToKeysym(X11->display, map->modifiermap[mapIndex], x++);
+                            } while (sym == NoSymbol && x < coreDesc.keysyms_per_keycode);
+                            const uchar mask = 1 << maskIndex;
+                            SETMASK(sym, mask);
+                        }
+                        mapIndex++;
+                    }
+                }
+
+                // determine the meaning of the Lock modifier
+                for (i = 0; i < map->max_keypermod; ++i) {
+                    for (int x = 0; x < coreDesc.keysyms_per_keycode; ++x) {
+                        KeySym sym = XKeycodeToKeysym(X11->display, map->modifiermap[LockMapIndex], x);
+                        if (sym == XK_Caps_Lock || sym == XK_ISO_Lock) {
+                            coreDesc.lock_meaning = XK_Caps_Lock;
+                            break;
+                        } else if (sym == XK_Shift_Lock) {
+                            coreDesc.lock_meaning = XK_Shift_Lock;
+                        }
+                    }
+                }
+
+                XFreeModifiermap(map);
+            }
+
+            // for qt_XTranslateKey()
+            coreDesc.num_lock = qt_num_lock_mask;
+            coreDesc.mode_switch = qt_mode_switch_mask;
+
+#if 0
+            qDebug() << "lock_meaning =" << coreDesc.lock_meaning;
+            qDebug() << "num_lock =" << coreDesc.num_lock;
+            qDebug() << "mode_switch =" << coreDesc.mode_switch;
+#endif
+        }
+
+    // set default modifier masks if needed
+    if( qt_alt_mask == 0 )
+        qt_alt_mask = Mod1Mask;
+    if( qt_meta_mask == 0 )
+        qt_meta_mask = Mod4Mask;
+
+    // if we don't have a meta key (or it's hidden behind alt), use super or hyper to generate
+    // Qt::Key_Meta and Qt::MetaModifier, since most newer XFree86/Xorg installations map the Windows
+    // key to Super
+    if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) {
+        // no meta keys... s,meta,super,
+        qt_meta_mask = qt_super_mask;
+        if (qt_meta_mask == 0 || qt_meta_mask == qt_alt_mask) {
+            // no super keys either? guess we'll use hyper then
+            qt_meta_mask = qt_hyper_mask;
+        }
+    }
+
+#if 0
+    qDebug() << "qt_alt_mask =" << hex << qt_alt_mask;
+    qDebug() << "qt_meta_mask =" << hex << qt_meta_mask;
+    qDebug() << "qt_super_mask =" << hex << qt_super_mask;
+    qDebug() << "qt_hyper_mask =" << hex << qt_hyper_mask;
+    qDebug() << "qt_mode_switch_mask =" << hex << qt_mode_switch_mask;
+    qDebug() << "qt_num_lock_mask =" << hex << qt_num_lock_mask;
+#endif
+}
+
+extern bool qt_sm_blockUserInput;
+
+//
+// Keyboard event translation
+//
+
+#ifndef XK_ISO_Left_Tab
+#define XK_ISO_Left_Tab         0xFE20
+#endif
+
+#ifndef XK_dead_hook
+#define XK_dead_hook            0xFE61
+#endif
+
+#ifndef XK_dead_horn
+#define XK_dead_horn            0xFE62
+#endif
+
+#ifndef XK_Codeinput
+#define XK_Codeinput            0xFF37
+#endif
+
+#ifndef XK_Kanji_Bangou
+#define XK_Kanji_Bangou         0xFF37 /* same as codeinput */
+#endif
+
+// Fix old X libraries
+#ifndef XK_KP_Home
+#define XK_KP_Home              0xFF95
+#endif
+#ifndef XK_KP_Left
+#define XK_KP_Left              0xFF96
+#endif
+#ifndef XK_KP_Up
+#define XK_KP_Up                0xFF97
+#endif
+#ifndef XK_KP_Right
+#define XK_KP_Right             0xFF98
+#endif
+#ifndef XK_KP_Down
+#define XK_KP_Down              0xFF99
+#endif
+#ifndef XK_KP_Prior
+#define XK_KP_Prior             0xFF9A
+#endif
+#ifndef XK_KP_Next
+#define XK_KP_Next              0xFF9B
+#endif
+#ifndef XK_KP_End
+#define XK_KP_End               0xFF9C
+#endif
+#ifndef XK_KP_Insert
+#define XK_KP_Insert            0xFF9E
+#endif
+#ifndef XK_KP_Delete
+#define XK_KP_Delete            0xFF9F
+#endif
+
+// the next lines are taken from XFree > 4.0 (X11/XF86keysyms.h), defining some special
+// multimedia keys. They are included here as not every system has them.
+#define XF86XK_Standby          0x1008FF10
+#define XF86XK_AudioLowerVolume 0x1008FF11
+#define XF86XK_AudioMute        0x1008FF12
+#define XF86XK_AudioRaiseVolume 0x1008FF13
+#define XF86XK_AudioPlay        0x1008FF14
+#define XF86XK_AudioStop        0x1008FF15
+#define XF86XK_AudioPrev        0x1008FF16
+#define XF86XK_AudioNext        0x1008FF17
+#define XF86XK_HomePage         0x1008FF18
+#define XF86XK_Calculator       0x1008FF1D
+#define XF86XK_Mail             0x1008FF19
+#define XF86XK_Start            0x1008FF1A
+#define XF86XK_Search           0x1008FF1B
+#define XF86XK_AudioRecord      0x1008FF1C
+#define XF86XK_Back             0x1008FF26
+#define XF86XK_Forward          0x1008FF27
+#define XF86XK_Stop             0x1008FF28
+#define XF86XK_Refresh          0x1008FF29
+#define XF86XK_Favorites        0x1008FF30
+#define XF86XK_AudioPause       0x1008FF31
+#define XF86XK_AudioMedia       0x1008FF32
+#define XF86XK_MyComputer       0x1008FF33
+#define XF86XK_OpenURL          0x1008FF38
+#define XF86XK_Launch0          0x1008FF40
+#define XF86XK_Launch1          0x1008FF41
+#define XF86XK_Launch2          0x1008FF42
+#define XF86XK_Launch3          0x1008FF43
+#define XF86XK_Launch4          0x1008FF44
+#define XF86XK_Launch5          0x1008FF45
+#define XF86XK_Launch6          0x1008FF46
+#define XF86XK_Launch7          0x1008FF47
+#define XF86XK_Launch8          0x1008FF48
+#define XF86XK_Launch9          0x1008FF49
+#define XF86XK_LaunchA          0x1008FF4A
+#define XF86XK_LaunchB          0x1008FF4B
+#define XF86XK_LaunchC          0x1008FF4C
+#define XF86XK_LaunchD          0x1008FF4D
+#define XF86XK_LaunchE          0x1008FF4E
+#define XF86XK_LaunchF          0x1008FF4F
+// end of XF86keysyms.h
+
+// Special keys used by Qtopia, mapped into the X11 private keypad range.
+#define QTOPIAXK_Select         0x11000601
+#define QTOPIAXK_Yes            0x11000602
+#define QTOPIAXK_No             0x11000603
+#define QTOPIAXK_Cancel         0x11000604
+#define QTOPIAXK_Printer        0x11000605
+#define QTOPIAXK_Execute        0x11000606
+#define QTOPIAXK_Sleep          0x11000607
+#define QTOPIAXK_Play           0x11000608
+#define QTOPIAXK_Zoom           0x11000609
+#define QTOPIAXK_Context1       0x1100060A
+#define QTOPIAXK_Context2       0x1100060B
+#define QTOPIAXK_Context3       0x1100060C
+#define QTOPIAXK_Context4       0x1100060D
+#define QTOPIAXK_Call           0x1100060E
+#define QTOPIAXK_Hangup         0x1100060F
+#define QTOPIAXK_Flip           0x11000610
+
+// keyboard mapping table
+static const unsigned int KeyTbl[] = {
+
+    // misc keys
+
+    XK_Escape,                  Qt::Key_Escape,
+    XK_Tab,                     Qt::Key_Tab,
+    XK_ISO_Left_Tab,            Qt::Key_Backtab,
+    XK_BackSpace,               Qt::Key_Backspace,
+    XK_Return,                  Qt::Key_Return,
+    XK_Insert,                  Qt::Key_Insert,
+    XK_Delete,                  Qt::Key_Delete,
+    XK_Clear,                   Qt::Key_Delete,
+    XK_Pause,                   Qt::Key_Pause,
+    XK_Print,                   Qt::Key_Print,
+    0x1005FF60,                 Qt::Key_SysReq,         // hardcoded Sun SysReq
+    0x1007ff00,                 Qt::Key_SysReq,         // hardcoded X386 SysReq
+
+    // cursor movement
+
+    XK_Home,                    Qt::Key_Home,
+    XK_End,                     Qt::Key_End,
+    XK_Left,                    Qt::Key_Left,
+    XK_Up,                      Qt::Key_Up,
+    XK_Right,                   Qt::Key_Right,
+    XK_Down,                    Qt::Key_Down,
+    XK_Prior,                   Qt::Key_PageUp,
+    XK_Next,                    Qt::Key_PageDown,
+
+    // modifiers
+
+    XK_Shift_L,                 Qt::Key_Shift,
+    XK_Shift_R,                 Qt::Key_Shift,
+    XK_Shift_Lock,              Qt::Key_Shift,
+    XK_Control_L,               Qt::Key_Control,
+    XK_Control_R,               Qt::Key_Control,
+    XK_Meta_L,                  Qt::Key_Meta,
+    XK_Meta_R,                  Qt::Key_Meta,
+    XK_Alt_L,                   Qt::Key_Alt,
+    XK_Alt_R,                   Qt::Key_Alt,
+    XK_Caps_Lock,               Qt::Key_CapsLock,
+    XK_Num_Lock,                Qt::Key_NumLock,
+    XK_Scroll_Lock,             Qt::Key_ScrollLock,
+    XK_Super_L,                 Qt::Key_Super_L,
+    XK_Super_R,                 Qt::Key_Super_R,
+    XK_Menu,                    Qt::Key_Menu,
+    XK_Hyper_L,                 Qt::Key_Hyper_L,
+    XK_Hyper_R,                 Qt::Key_Hyper_R,
+    XK_Help,                    Qt::Key_Help,
+    0x1000FF74,                 Qt::Key_Backtab,        // hardcoded HP backtab
+    0x1005FF10,                 Qt::Key_F11,            // hardcoded Sun F36 (labeled F11)
+    0x1005FF11,                 Qt::Key_F12,            // hardcoded Sun F37 (labeled F12)
+
+    // numeric and function keypad keys
+
+    XK_KP_Space,                Qt::Key_Space,
+    XK_KP_Tab,                  Qt::Key_Tab,
+    XK_KP_Enter,                Qt::Key_Enter,
+    //XK_KP_F1,                 Qt::Key_F1,
+    //XK_KP_F2,                 Qt::Key_F2,
+    //XK_KP_F3,                 Qt::Key_F3,
+    //XK_KP_F4,                 Qt::Key_F4,
+    XK_KP_Home,                 Qt::Key_Home,
+    XK_KP_Left,                 Qt::Key_Left,
+    XK_KP_Up,                   Qt::Key_Up,
+    XK_KP_Right,                Qt::Key_Right,
+    XK_KP_Down,                 Qt::Key_Down,
+    XK_KP_Prior,                Qt::Key_PageUp,
+    XK_KP_Next,                 Qt::Key_PageDown,
+    XK_KP_End,                  Qt::Key_End,
+    XK_KP_Begin,                Qt::Key_Clear,
+    XK_KP_Insert,               Qt::Key_Insert,
+    XK_KP_Delete,               Qt::Key_Delete,
+    XK_KP_Equal,                Qt::Key_Equal,
+    XK_KP_Multiply,             Qt::Key_Asterisk,
+    XK_KP_Add,                  Qt::Key_Plus,
+    XK_KP_Separator,            Qt::Key_Comma,
+    XK_KP_Subtract,             Qt::Key_Minus,
+    XK_KP_Decimal,              Qt::Key_Period,
+    XK_KP_Divide,               Qt::Key_Slash,
+
+    // International input method support keys
+
+    // International & multi-key character composition
+    XK_ISO_Level3_Shift,        Qt::Key_AltGr,
+    XK_Multi_key,		Qt::Key_Multi_key,
+    XK_Codeinput,		Qt::Key_Codeinput,
+    XK_SingleCandidate,		Qt::Key_SingleCandidate,
+    XK_MultipleCandidate,	Qt::Key_MultipleCandidate,
+    XK_PreviousCandidate,	Qt::Key_PreviousCandidate,
+
+    // Misc Functions
+    XK_Mode_switch,		Qt::Key_Mode_switch,
+    XK_script_switch,		Qt::Key_Mode_switch,
+
+    // Japanese keyboard support
+    XK_Kanji,			Qt::Key_Kanji,
+    XK_Muhenkan,		Qt::Key_Muhenkan,
+    //XK_Henkan_Mode,		Qt::Key_Henkan_Mode,
+    XK_Henkan_Mode,		Qt::Key_Henkan,
+    XK_Henkan,			Qt::Key_Henkan,
+    XK_Romaji,			Qt::Key_Romaji,
+    XK_Hiragana,		Qt::Key_Hiragana,
+    XK_Katakana,		Qt::Key_Katakana,
+    XK_Hiragana_Katakana,	Qt::Key_Hiragana_Katakana,
+    XK_Zenkaku,			Qt::Key_Zenkaku,
+    XK_Hankaku,			Qt::Key_Hankaku,
+    XK_Zenkaku_Hankaku,		Qt::Key_Zenkaku_Hankaku,
+    XK_Touroku,			Qt::Key_Touroku,
+    XK_Massyo,			Qt::Key_Massyo,
+    XK_Kana_Lock,		Qt::Key_Kana_Lock,
+    XK_Kana_Shift,		Qt::Key_Kana_Shift,
+    XK_Eisu_Shift,		Qt::Key_Eisu_Shift,
+    XK_Eisu_toggle,		Qt::Key_Eisu_toggle,
+    //XK_Kanji_Bangou,		Qt::Key_Kanji_Bangou,
+    //XK_Zen_Koho,		Qt::Key_Zen_Koho,
+    //XK_Mae_Koho,		Qt::Key_Mae_Koho,
+    XK_Kanji_Bangou,		Qt::Key_Codeinput,
+    XK_Zen_Koho,		Qt::Key_MultipleCandidate,
+    XK_Mae_Koho,		Qt::Key_PreviousCandidate,
+
+#ifdef XK_KOREAN
+    // Korean keyboard support
+    XK_Hangul,			Qt::Key_Hangul,
+    XK_Hangul_Start,		Qt::Key_Hangul_Start,
+    XK_Hangul_End,		Qt::Key_Hangul_End,
+    XK_Hangul_Hanja,		Qt::Key_Hangul_Hanja,
+    XK_Hangul_Jamo,		Qt::Key_Hangul_Jamo,
+    XK_Hangul_Romaja,		Qt::Key_Hangul_Romaja,
+    //XK_Hangul_Codeinput,	Qt::Key_Hangul_Codeinput,
+    XK_Hangul_Codeinput,	Qt::Key_Codeinput,
+    XK_Hangul_Jeonja,		Qt::Key_Hangul_Jeonja,
+    XK_Hangul_Banja,		Qt::Key_Hangul_Banja,
+    XK_Hangul_PreHanja,		Qt::Key_Hangul_PreHanja,
+    XK_Hangul_PostHanja,	Qt::Key_Hangul_PostHanja,
+    //XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate,
+    //XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate,
+    //XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate,
+    XK_Hangul_SingleCandidate,	Qt::Key_SingleCandidate,
+    XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate,
+    XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate,
+    XK_Hangul_Special,		Qt::Key_Hangul_Special,
+    //XK_Hangul_switch,		Qt::Key_Hangul_switch,
+    XK_Hangul_switch,		Qt::Key_Mode_switch,
+#endif  // XK_KOREAN
+
+    // dead keys
+    XK_dead_grave,              Qt::Key_Dead_Grave,
+    XK_dead_acute,              Qt::Key_Dead_Acute,
+    XK_dead_circumflex,         Qt::Key_Dead_Circumflex,
+    XK_dead_tilde,              Qt::Key_Dead_Tilde,
+    XK_dead_macron,             Qt::Key_Dead_Macron,
+    XK_dead_breve,              Qt::Key_Dead_Breve,
+    XK_dead_abovedot,           Qt::Key_Dead_Abovedot,
+    XK_dead_diaeresis,          Qt::Key_Dead_Diaeresis,
+    XK_dead_abovering,          Qt::Key_Dead_Abovering,
+    XK_dead_doubleacute,        Qt::Key_Dead_Doubleacute,
+    XK_dead_caron,              Qt::Key_Dead_Caron,
+    XK_dead_cedilla,            Qt::Key_Dead_Cedilla,
+    XK_dead_ogonek,             Qt::Key_Dead_Ogonek,
+    XK_dead_iota,               Qt::Key_Dead_Iota,
+    XK_dead_voiced_sound,       Qt::Key_Dead_Voiced_Sound,
+    XK_dead_semivoiced_sound,   Qt::Key_Dead_Semivoiced_Sound,
+    XK_dead_belowdot,           Qt::Key_Dead_Belowdot,
+    XK_dead_hook,               Qt::Key_Dead_Hook,
+    XK_dead_horn,               Qt::Key_Dead_Horn,
+
+    // Special multimedia keys
+    // currently only tested with MS internet keyboard
+
+    // browsing keys
+    XF86XK_Back,                Qt::Key_Back,
+    XF86XK_Forward,             Qt::Key_Forward,
+    XF86XK_Stop,                Qt::Key_Stop,
+    XF86XK_Refresh,             Qt::Key_Refresh,
+    XF86XK_Favorites,           Qt::Key_Favorites,
+    XF86XK_AudioMedia,          Qt::Key_LaunchMedia,
+    XF86XK_OpenURL,             Qt::Key_OpenUrl,
+    XF86XK_HomePage,            Qt::Key_HomePage,
+    XF86XK_Search,              Qt::Key_Search,
+
+    // media keys
+    XF86XK_AudioLowerVolume,    Qt::Key_VolumeDown,
+    XF86XK_AudioMute,           Qt::Key_VolumeMute,
+    XF86XK_AudioRaiseVolume,    Qt::Key_VolumeUp,
+    XF86XK_AudioPlay,           Qt::Key_MediaPlay,
+    XF86XK_AudioStop,           Qt::Key_MediaStop,
+    XF86XK_AudioPrev,           Qt::Key_MediaPrevious,
+    XF86XK_AudioNext,           Qt::Key_MediaNext,
+    XF86XK_AudioRecord,         Qt::Key_MediaRecord,
+
+    // launch keys
+    XF86XK_Mail,                Qt::Key_LaunchMail,
+    XF86XK_MyComputer,          Qt::Key_Launch0,
+    XF86XK_Calculator,          Qt::Key_Launch1,
+    XF86XK_Standby,             Qt::Key_Standby,
+
+    XF86XK_Launch0,             Qt::Key_Launch2,
+    XF86XK_Launch1,             Qt::Key_Launch3,
+    XF86XK_Launch2,             Qt::Key_Launch4,
+    XF86XK_Launch3,             Qt::Key_Launch5,
+    XF86XK_Launch4,             Qt::Key_Launch6,
+    XF86XK_Launch5,             Qt::Key_Launch7,
+    XF86XK_Launch6,             Qt::Key_Launch8,
+    XF86XK_Launch7,             Qt::Key_Launch9,
+    XF86XK_Launch8,             Qt::Key_LaunchA,
+    XF86XK_Launch9,             Qt::Key_LaunchB,
+    XF86XK_LaunchA,             Qt::Key_LaunchC,
+    XF86XK_LaunchB,             Qt::Key_LaunchD,
+    XF86XK_LaunchC,             Qt::Key_LaunchE,
+    XF86XK_LaunchD,             Qt::Key_LaunchF,
+
+    // Qtopia keys
+    QTOPIAXK_Select,            Qt::Key_Select,
+    QTOPIAXK_Yes,               Qt::Key_Yes,
+    QTOPIAXK_No,                Qt::Key_No,
+    QTOPIAXK_Cancel,            Qt::Key_Cancel,
+    QTOPIAXK_Printer,           Qt::Key_Printer,
+    QTOPIAXK_Execute,           Qt::Key_Execute,
+    QTOPIAXK_Sleep,             Qt::Key_Sleep,
+    QTOPIAXK_Play,              Qt::Key_Play,
+    QTOPIAXK_Zoom,              Qt::Key_Zoom,
+    QTOPIAXK_Context1,          Qt::Key_Context1,
+    QTOPIAXK_Context2,          Qt::Key_Context2,
+    QTOPIAXK_Context3,          Qt::Key_Context3,
+    QTOPIAXK_Context4,          Qt::Key_Context4,
+    QTOPIAXK_Call,              Qt::Key_Call,
+    QTOPIAXK_Hangup,            Qt::Key_Hangup,
+    QTOPIAXK_Flip,              Qt::Key_Flip,
+
+    0,                          0
+};
+
+static int translateKeySym(uint key)
+{
+    int code = -1;
+    int i = 0;                                // any other keys
+    while (KeyTbl[i]) {
+        if (key == KeyTbl[i]) {
+            code = (int)KeyTbl[i+1];
+            break;
+        }
+        i += 2;
+    }
+    if (qt_meta_mask) {
+        // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
+        if (qt_meta_mask == qt_super_mask && (code == Qt::Key_Super_L || code == Qt::Key_Super_R)) {
+            code = Qt::Key_Meta;
+        } else if (qt_meta_mask == qt_hyper_mask && (code == Qt::Key_Hyper_L || code == Qt::Key_Hyper_R)) {
+            code = Qt::Key_Meta;
+        }
+    }
+    return code;
+}
+
+#if !defined(QT_NO_XIM)
+static const unsigned short katakanaKeysymsToUnicode[] = {
+    0x0000, 0x3002, 0x300C, 0x300D, 0x3001, 0x30FB, 0x30F2, 0x30A1,
+    0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30E3, 0x30E5, 0x30E7, 0x30C3,
+    0x30FC, 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD,
+    0x30AF, 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD,
+    0x30BF, 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC,
+    0x30CD, 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE,
+    0x30DF, 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9,
+    0x30EA, 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F3, 0x309B, 0x309C
+};
+
+static const unsigned short cyrillicKeysymsToUnicode[] = {
+    0x0000, 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457,
+    0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f,
+    0x2116, 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407,
+    0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f,
+    0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
+    0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
+    0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
+    0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,
+    0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
+    0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,
+    0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
+    0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a
+};
+
+static const unsigned short greekKeysymsToUnicode[] = {
+    0x0000, 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c,
+    0x038e, 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015,
+    0x0000, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc,
+    0x03cd, 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+    0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
+    0x03a0, 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+    0x03a8, 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+    0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+    0x03c0, 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+    0x03c8, 0x03c9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short technicalKeysymsToUnicode[] = {
+    0x0000, 0x23B7, 0x250C, 0x2500, 0x2320, 0x2321, 0x2502, 0x23A1,
+    0x23A3, 0x23A4, 0x23A6, 0x239B, 0x239D, 0x239E, 0x23A0, 0x23A8,
+    0x23AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222B,
+    0x2234, 0x221D, 0x221E, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000,
+    0x223C, 0x2243, 0x0000, 0x0000, 0x0000, 0x21D4, 0x21D2, 0x2261,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221A, 0x0000,
+    0x0000, 0x0000, 0x2282, 0x2283, 0x2229, 0x222A, 0x2227, 0x2228,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193, 0x0000
+};
+
+static const unsigned short specialKeysymsToUnicode[] = {
+    0x25C6, 0x2592, 0x2409, 0x240C, 0x240D, 0x240A, 0x0000, 0x0000,
+    0x2424, 0x240B, 0x2518, 0x2510, 0x250C, 0x2514, 0x253C, 0x23BA,
+    0x23BB, 0x2500, 0x23BC, 0x23BD, 0x251C, 0x2524, 0x2534, 0x252C,
+    0x2502, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short publishingKeysymsToUnicode[] = {
+    0x0000, 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009,
+    0x200a, 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025,
+    0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a,
+    0x2105, 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000,
+    0x0000, 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af,
+    0x2018, 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033,
+    0x0000, 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae,
+    0x25e6, 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa,
+    0x25b2, 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000,
+    0x2720, 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642,
+    0x2640, 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e, 0x0000
+};
+
+static const unsigned short aplKeysymsToUnicode[] = {
+    0x0000, 0x0000, 0x0000, 0x003c, 0x0000, 0x0000, 0x003e, 0x0000,
+    0x2228, 0x2227, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00af, 0x0000, 0x22a5, 0x2229, 0x230a, 0x0000, 0x005f, 0x0000,
+    0x0000, 0x0000, 0x2218, 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb,
+    0x0000, 0x0000, 0x0000, 0x2308, 0x0000, 0x0000, 0x222a, 0x0000,
+    0x2283, 0x0000, 0x2282, 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x22a3, 0x0000, 0x0000, 0x0000
+};
+
+static const unsigned short koreanKeysymsToUnicode[] = {
+    0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137,
+    0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f,
+    0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147,
+    0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f,
+    0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157,
+    0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f,
+    0x3160, 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab,
+    0x11ac, 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3,
+    0x11b4, 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb,
+    0x11bc, 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d,
+    0x3171, 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e,
+    0x11eb, 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9
+};
+
+static QChar keysymToUnicode(unsigned char byte3, unsigned char byte4)
+{
+    switch (byte3) {
+    case 0x04:
+        // katakana
+        if (byte4 > 0xa0 && byte4 < 0xe0)
+            return QChar(katakanaKeysymsToUnicode[byte4 - 0xa0]);
+        else if (byte4 == 0x7e)
+            return QChar(0x203e); // Overline
+        break;
+    case 0x06:
+        // russian, use lookup table
+        if (byte4 > 0xa0)
+            return QChar(cyrillicKeysymsToUnicode[byte4 - 0xa0]);
+        break;
+    case 0x07:
+        // greek
+        if (byte4 > 0xa0)
+            return QChar(greekKeysymsToUnicode[byte4 - 0xa0]);
+        break;
+    case 0x08:
+        // technical
+        if (byte4 > 0xa0)
+            return QChar(technicalKeysymsToUnicode[byte4 - 0xa0]);
+        break;
+    case 0x09:
+        // special
+        if (byte4 >= 0xe0)
+            return QChar(specialKeysymsToUnicode[byte4 - 0xe0]);
+        break;
+    case 0x0a:
+        // publishing
+        if (byte4 > 0xa0)
+            return QChar(publishingKeysymsToUnicode[byte4 - 0xa0]);
+        break;
+    case 0x0b:
+        // APL
+        if (byte4 > 0xa0)
+            return QChar(aplKeysymsToUnicode[byte4 - 0xa0]);
+        break;
+    case 0x0e:
+        // Korean
+        if (byte4 > 0xa0)
+            return QChar(koreanKeysymsToUnicode[byte4 - 0xa0]);
+        break;
+    default:
+        break;
+    }
+    return QChar(0x0);
+}
+#endif
+
+static QString translateKeySym(KeySym keysym, uint xmodifiers,
+                               int &code, Qt::KeyboardModifiers &modifiers,
+                               QByteArray &chars, int &count)
+{
+    // all keysyms smaller than 0xff00 are actally keys that can be mapped to unicode chars
+
+    extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp
+    QTextCodec *mapper = qt_input_mapper;
+    QChar converted;
+
+    if (count == 0 && keysym < 0xff00) {
+        unsigned char byte3 = (unsigned char)(keysym >> 8);
+        int mib = -1;
+        switch(byte3) {
+        case 0: // Latin 1
+        case 1: // Latin 2
+        case 2: //latin 3
+        case 3: // latin4
+            mib = byte3 + 4; break;
+        case 5: // arabic
+            mib = 82; break;
+        case 12: // Hebrew
+            mib = 85; break;
+        case 13: // Thai
+            mib = 2259; break;
+        case 4: // kana
+        case 6: // cyrillic
+        case 7: // greek
+        case 8: // technical, no mapping here at the moment
+        case 9: // Special
+        case 10: // Publishing
+        case 11: // APL
+        case 14: // Korean, no mapping
+            mib = -1; // manual conversion
+            mapper = 0;
+#if !defined(QT_NO_XIM)
+            converted = keysymToUnicode(byte3, keysym & 0xff);
+#endif
+        case 0x20:
+            // currency symbols
+            if (keysym >= 0x20a0 && keysym <= 0x20ac) {
+                mib = -1; // manual conversion
+                mapper = 0;
+                converted = (uint)keysym;
+            }
+            break;
+        default:
+            break;
+        }
+        if (mib != -1) {
+            mapper = QTextCodec::codecForMib(mib);
+            if (chars.isEmpty())
+                chars.resize(1);
+            chars[0] = (unsigned char) (keysym & 0xff); // get only the fourth bit for conversion later
+            count++;
+        }
+    } else if (keysym >= 0x1000000 && keysym <= 0x100ffff) {
+        converted = (ushort) (keysym - 0x1000000);
+        mapper = 0;
+    }
+    if (count < (int)chars.size()-1)
+        chars[count] = '\0';
+
+    QString text;
+    if (!mapper && converted.unicode() != 0x0) {
+        text = converted;
+    } else if (!chars.isEmpty()) {
+        // convert chars (8bit) to text (unicode).
+        if (mapper)
+            text = mapper->toUnicode(chars.data(), count, 0);
+        if (text.isEmpty()) {
+            // no mapper, or codec couldn't convert to unicode (this
+            // can happen when running in the C locale or with no LANG
+            // set). try converting from latin-1
+            text = QString::fromLatin1(chars);
+        }
+    }
+
+    modifiers = X11->translateModifiers(xmodifiers);
+
+    // Commentary in X11/keysymdef says that X codes match ASCII, so it
+    // is safe to use the locale functions to process X codes in ISO8859-1.
+    //
+    // This is mainly for compatibility - applications should not use the
+    // Qt keycodes between 128 and 255, but should rather use the
+    // QKeyEvent::text().
+    //
+    extern QTextCodec *qt_input_mapper; // from qapplication_x11.cpp
+    if (keysym < 128 || (keysym < 256 && (!qt_input_mapper || qt_input_mapper->mibEnum()==4))) {
+        // upper-case key, if known
+        code = isprint((int)keysym) ? toupper((int)keysym) : 0;
+    } else if (keysym >= XK_F1 && keysym <= XK_F35) {
+        // function keys
+        code = Qt::Key_F1 + ((int)keysym - XK_F1);
+    } else if (keysym >= XK_KP_Space && keysym <= XK_KP_9) {
+        if (keysym >= XK_KP_0) {
+            // numeric keypad keys
+            code = Qt::Key_0 + ((int)keysym - XK_KP_0);
+        } else {
+            code = translateKeySym(keysym);
+        }
+        modifiers |= Qt::KeypadModifier;
+    } else if (text.length() == 1 && text.unicode()->unicode() > 0x1f && text.unicode()->unicode() != 0x7f && !(keysym >= XK_dead_grave && keysym <= XK_dead_horn)) {
+        code = text.unicode()->toUpper().unicode();
+    } else {
+        // any other keys
+        code = translateKeySym(keysym);
+
+        if (code == Qt::Key_Tab && (modifiers & Qt::ShiftModifier)) {
+            // map shift+tab to shift+backtab, QShortcutMap knows about it
+            // and will handle it.
+            code = Qt::Key_Backtab;
+            text = QString();
+        }
+    }
+
+    return text;
+}
+
+extern bool qt_use_rtl_extensions; // from qapplication_x11.cpp
+
+bool QKeyMapperPrivate::translateKeyEventInternal(QWidget *keyWidget,
+                                                  const XEvent *event,
+                                                  KeySym &keysym,
+                                                  int& count,
+                                                  QString& text,
+                                                  Qt::KeyboardModifiers &modifiers,
+                                                  int& code,
+                                                  QEvent::Type &type,
+                                                  bool statefulTranslation)
+{
+    XKeyEvent xkeyevent = event->xkey;
+    int keycode = event->xkey.keycode;
+    // save the modifier state, we will use the keystate uint later by passing
+    // it to translateButtonState
+    uint keystate = event->xkey.state;
+
+    type = (event->type == XKeyPress) ? QEvent::KeyPress : QEvent::KeyRelease;
+
+    static int directionKeyEvent = 0;
+    static unsigned int lastWinId = 0;
+
+    // translate pending direction change
+    if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyRelease) {
+        if (directionKeyEvent == Qt::Key_Direction_R || directionKeyEvent == Qt::Key_Direction_L) {
+            type = QEvent::KeyPress;
+            code = directionKeyEvent;
+            text = QString();
+            directionKeyEvent = 0;
+	    lastWinId = 0;
+            return true;
+        } else {
+            directionKeyEvent = 0;
+	    lastWinId = 0;
+        }
+    }
+
+    // some XmbLookupString implementations don't return buffer overflow correctly,
+    // so we increase the input buffer to allow for long strings...
+    // 256 chars * 2 bytes + 1 null-term == 513 bytes
+    QByteArray chars;
+    chars.resize(513);
+
+    count = XLookupString(&xkeyevent, chars.data(), chars.size(), &keysym, 0);
+    if (count && !keycode) {
+        extern int qt_ximComposingKeycode; // from qapplication_x11.cpp
+        keycode = qt_ximComposingKeycode;
+        qt_ximComposingKeycode = 0;
+    }
+
+    // translate the keysym + xmodifiers to Qt::Key_* + Qt::KeyboardModifiers
+    text = translateKeySym(keysym, keystate, code, modifiers, chars, count);
+
+    // Watch for keypresses and if its a key belonging to the Ctrl-Shift
+    // direction-changing accel, remember it.
+    // We keep track of those keys instead of using the event's state
+    // (to figure out whether the Ctrl modifier is held while Shift is pressed,
+    // or Shift is held while Ctrl is pressed) since the 'state' doesn't tell
+    // us whether the modifier held is Left or Right.
+    if (statefulTranslation && qt_use_rtl_extensions && type == QEvent::KeyPress) {
+        if (keysym == XK_Control_L || keysym == XK_Control_R
+            || keysym == XK_Shift_L || keysym == XK_Shift_R) {
+	    if (!directionKeyEvent) {
+		directionKeyEvent = keysym;
+		// This code exists in order to check that
+		// the event is occurred in the same widget.
+		lastWinId = keyWidget->internalWinId();
+	    }
+        } else {
+            // this can no longer be a direction-changing accel.
+            // if any other key was pressed.
+            directionKeyEvent = Qt::Key_Space;
+        }
+
+        if (directionKeyEvent && lastWinId == keyWidget->internalWinId()) {
+            if ((keysym == XK_Shift_L && directionKeyEvent == XK_Control_L)
+                || (keysym == XK_Control_L && directionKeyEvent == XK_Shift_L)) {
+                directionKeyEvent = Qt::Key_Direction_L;
+            } else if ((keysym == XK_Shift_R && directionKeyEvent == XK_Control_R)
+                       || (keysym == XK_Control_R && directionKeyEvent == XK_Shift_R)) {
+                directionKeyEvent = Qt::Key_Direction_R;
+            }
+        } else if (directionKeyEvent == Qt::Key_Direction_L
+                   || directionKeyEvent == Qt::Key_Direction_R) {
+            directionKeyEvent = Qt::Key_Space; // invalid
+        }
+    }
+
+    return true;
+}
+
+
+struct qt_auto_repeat_data
+{
+    // match the window and keycode with timestamp delta of 10 ms
+    Window window;
+    KeyCode keycode;
+    Time timestamp;
+
+    // queue scanner state
+    bool release;
+    bool error;
+};
+
+#if defined(Q_C_CALLBACKS)
+extern "C" {
+#endif
+
+static Bool qt_keypress_scanner(Display *, XEvent *event, XPointer arg)
+{
+    if (event->type != XKeyPress && event->type != XKeyRelease)
+        return false;
+
+    qt_auto_repeat_data *data = (qt_auto_repeat_data *) arg;
+    if (data->error)
+        return false;
+
+    if (event->xkey.window  != data->window ||
+        event->xkey.keycode != data->keycode) {
+        // deal breakers: key events in a different window or an event
+        // with a different key code
+        data->error = true;
+        return false;
+    }
+
+    if (event->type == XKeyPress) {
+        data->error = (! data->release || event->xkey.time - data->timestamp > 10);
+        return (! data->error);
+    }
+
+    // must be XKeyRelease event
+    if (data->release) {
+        // found a second release
+        data->error = true;
+        return false;
+    }
+
+    // found a single release
+    data->release = true;
+    data->timestamp = event->xkey.time;
+
+    return false;
+}
+
+static Bool qt_keyrelease_scanner(Display *, XEvent *event, XPointer arg)
+{
+    const qt_auto_repeat_data *data = (const qt_auto_repeat_data *) arg;
+    return (event->type == XKeyRelease &&
+            event->xkey.window  == data->window &&
+            event->xkey.keycode == data->keycode);
+}
+
+#if defined(Q_C_CALLBACKS)
+}
+#endif
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *keyWidget, const XEvent *event, bool grab)
+{
+    int           code = -1;
+    int           count = 0;
+    Qt::KeyboardModifiers modifiers;
+
+    if (qt_sm_blockUserInput) // block user interaction during session management
+        return true;
+
+    Display *dpy = X11->display;
+
+    if (!keyWidget->isEnabled())
+        return true;
+
+    QEvent::Type type;
+    bool    autor = false;
+    QString text;
+
+    KeySym keysym = 0;
+    translateKeyEventInternal(keyWidget, event, keysym, count, text, modifiers, code, type);
+
+    // was this the last auto-repeater?
+    qt_auto_repeat_data auto_repeat_data;
+    auto_repeat_data.window = event->xkey.window;
+    auto_repeat_data.keycode = event->xkey.keycode;
+    auto_repeat_data.timestamp = event->xkey.time;
+
+    static uint curr_autorep = 0;
+    if (event->type == XKeyPress) {
+        if (curr_autorep == event->xkey.keycode) {
+            autor = true;
+            curr_autorep = 0;
+        }
+    } else {
+        // look ahead for auto-repeat
+        XEvent nextpress;
+
+        auto_repeat_data.release = true;
+        auto_repeat_data.error = false;
+        if (XCheckIfEvent(dpy, &nextpress, &qt_keypress_scanner,
+                          (XPointer) &auto_repeat_data)) {
+            autor = true;
+
+            // Put it back... we COULD send the event now and not need
+            // the static curr_autorep variable.
+            XPutBackEvent(dpy,&nextpress);
+        }
+        curr_autorep = autor ? event->xkey.keycode : 0;
+    }
+
+#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
+    // process accelerators before doing key compression
+    if (type == QEvent::KeyPress && !grab
+        && QApplicationPrivate::instance()->use_compat()) {
+        // send accel events if the keyboard is not grabbed
+        QKeyEventEx a(type, code, modifiers, text, autor, qMax(qMax(count,1), int(text.length())),
+                      event->xkey.keycode, keysym, event->xkey.state);
+        if (QApplicationPrivate::instance()->qt_tryAccelEvent(keyWidget, &a))
+            return true;
+    }
+#endif
+
+#ifndef QT_NO_IM
+    QInputContext *qic = keyWidget->inputContext();
+#endif
+
+    // compress keys
+    if (!text.isEmpty() && keyWidget->testAttribute(Qt::WA_KeyCompression) &&
+#ifndef QT_NO_IM
+        // Ordinary input methods require discrete key events to work
+        // properly, so key compression has to be disabled when input
+        // context exists.
+        //
+        // And further consideration, some complex input method
+        // require all key press/release events discretely even if
+        // the input method awares of key compression and compressed
+        // keys are ordinary alphabets. For example, the uim project
+        // is planning to implement "combinational shift" feature for
+        // a Japanese input method, uim-skk. It will work as follows.
+        //
+        // 1. press "r"
+        // 2. press "u"
+        // 3. release both "r" and "u" in arbitrary order
+        // 4. above key sequence generates "Ru"
+        //
+        // Of course further consideration about other participants
+        // such as key repeat mechanism is required to implement such
+        // feature.
+        !qic &&
+#endif // QT_NO_IM
+        // do not compress keys if the key event we just got above matches
+        // one of the key ranges used to compute stopCompression
+        !((code >= Qt::Key_Escape && code <= Qt::Key_SysReq)
+          || (code >= Qt::Key_Home && code <= Qt::Key_PageDown)
+          || (code >= Qt::Key_Super_L && code <= Qt::Key_Direction_R)
+          || (code == 0)
+          || (text.length() == 1 && text.unicode()->unicode() == '\n'))) {
+        // the widget wants key compression so it gets it
+
+        // sync the event queue, this makes key compress work better
+        XSync(dpy, false);
+
+        for (;;) {
+            XEvent        evRelease;
+            XEvent        evPress;
+            if (!XCheckTypedWindowEvent(dpy,event->xkey.window,
+                                        XKeyRelease,&evRelease))
+                break;
+            if (!XCheckTypedWindowEvent(dpy,event->xkey.window,
+                                        XKeyPress,&evPress)) {
+                XPutBackEvent(dpy, &evRelease);
+                break;
+            }
+            QString textIntern;
+            int codeIntern = -1;
+            int countIntern = 0;
+            Qt::KeyboardModifiers modifiersIntern;
+            QEvent::Type t;
+            KeySym keySymIntern;
+            translateKeyEventInternal(keyWidget, &evPress, keySymIntern, countIntern, textIntern,
+                                      modifiersIntern, codeIntern, t);
+            // use stopCompression to stop key compression for the following
+            // key event ranges:
+            bool stopCompression =
+                // 1) misc keys
+                (codeIntern >= Qt::Key_Escape && codeIntern <= Qt::Key_SysReq)
+                // 2) cursor movement
+                || (codeIntern >= Qt::Key_Home && codeIntern <= Qt::Key_PageDown)
+                // 3) extra keys
+                || (codeIntern >= Qt::Key_Super_L && codeIntern <= Qt::Key_Direction_R)
+                // 4) something that a) doesn't translate to text or b) translates
+                //    to newline text
+                || (codeIntern == 0)
+                || (textIntern.length() == 1 && textIntern.unicode()->unicode() == '\n')
+                || (codeIntern == Qt::Key_unknown);
+
+            if (modifiersIntern == modifiers && !textIntern.isEmpty() && !stopCompression) {
+                text += textIntern;
+                count += countIntern;
+            } else {
+                XPutBackEvent(dpy, &evPress);
+                XPutBackEvent(dpy, &evRelease);
+                break;
+            }
+        }
+    }
+
+    // autorepeat compression makes sense for all widgets (Windows
+    // does it automatically ....)
+    if (event->type == XKeyPress && text.length() <= 1
+#ifndef QT_NO_IM
+        // input methods need discrete key events
+        && !qic
+#endif// QT_NO_IM
+	) {
+        XEvent dummy;
+
+        for (;;) {
+            auto_repeat_data.release = false;
+            auto_repeat_data.error = false;
+            if (! XCheckIfEvent(dpy, &dummy, &qt_keypress_scanner,
+                                (XPointer) &auto_repeat_data))
+                break;
+            if (! XCheckIfEvent(dpy, &dummy, &qt_keyrelease_scanner,
+                                (XPointer) &auto_repeat_data))
+                break;
+
+            count++;
+            if (!text.isEmpty())
+                text += text[0];
+        }
+    }
+
+    return QKeyMapper::sendKeyEvent(keyWidget, grab, type, code, modifiers, text, autor,
+                                    qMax(qMax(count,1), int(text.length())),
+                                    event->xkey.keycode, keysym, event->xkey.state);
+}
+
+bool QKeyMapper::sendKeyEvent(QWidget *keyWidget, bool grab,
+                              QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+                              const QString &text, bool autorepeat, int count,
+                              quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
+                              bool *)
+{
+    // try the menukey first
+    if (type == QEvent::KeyPress && code == Qt::Key_Menu) {
+        QVariant v = keyWidget->inputMethodQuery(Qt::ImMicroFocus);
+        QPoint globalPos;
+        QPoint pos;
+        if (v.isNull()) {
+            globalPos = QCursor::pos();
+            pos = keyWidget->mapFromGlobal(globalPos);
+        } else {
+            pos = v.toRect().center();
+            globalPos = keyWidget->mapToGlobal(pos);
+        }
+        QContextMenuEvent e(QContextMenuEvent::Keyboard, pos, globalPos);
+        qt_sendSpontaneousEvent(keyWidget, &e);
+        if(e.isAccepted())
+            return true;
+    }
+
+    Q_UNUSED(grab);
+    QKeyEventEx e(type, code, modifiers, text, autorepeat, qMax(qMax(count,1), int(text.length())),
+                  nativeScanCode, nativeVirtualKey, nativeModifiers);
+    return qt_sendSpontaneousEvent(keyWidget, &e);
+}
+
+QT_END_NAMESPACE