src/gui/kernel/qkeymapper_mac.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/kernel/qkeymapper_mac.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,965 @@
+/****************************************************************************
+**
+** 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 <private/qt_mac_p.h>
+#include <qdebug.h>
+#include <qevent.h>
+#include <private/qevent_p.h>
+#include <qtextcodec.h>
+#include <qapplication.h>
+#include <qinputcontext.h>
+#include <private/qkeymapper_p.h>
+#include <private/qapplication_p.h>
+#include <private/qmacinputcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_USE_NAMESPACE
+
+/*****************************************************************************
+  QKeyMapper debug facilities
+ *****************************************************************************/
+//#define DEBUG_KEY_BINDINGS
+//#define DEBUG_KEY_BINDINGS_MODIFIERS
+//#define DEBUG_KEY_MAPS
+
+/*****************************************************************************
+  Internal variables and functions
+ *****************************************************************************/
+bool qt_mac_eat_unicode_key = false;
+extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); //qapplication_mac.cpp
+
+Q_GUI_EXPORT void qt_mac_secure_keyboard(bool b)
+{
+    static bool secure = false;
+    if (b != secure){
+        b ? EnableSecureEventInput() : DisableSecureEventInput();
+        secure = b;
+    }
+}
+
+/*
+    \internal
+    A Mac KeyboardLayoutItem has 8 possible states:
+        1. Unmodified
+        2. Shift
+        3. Control
+        4. Control + Shift
+        5. Alt
+        6. Alt + Shift
+        7. Alt + Control
+        8. Alt + Control + Shift
+        9. Meta
+        10. Meta + Shift
+        11. Meta + Control
+        12. Meta + Control + Shift
+        13. Meta + Alt
+        14. Meta + Alt + Shift
+        15. Meta + Alt + Control
+        16. Meta + Alt + Control + Shift
+*/
+struct KeyboardLayoutItem {
+    bool dirty;
+    quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character
+};
+
+// Possible modifier states.
+// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()!
+static const Qt::KeyboardModifiers ModsTbl[] = {
+    Qt::NoModifier,                                             // 0
+    Qt::ShiftModifier,                                          // 1
+    Qt::ControlModifier,                                        // 2
+    Qt::ControlModifier | Qt::ShiftModifier,                    // 3
+    Qt::AltModifier,                                            // 4
+    Qt::AltModifier | Qt::ShiftModifier,                        // 5
+    Qt::AltModifier | Qt::ControlModifier,                      // 6
+    Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier,  // 7
+    Qt::MetaModifier,                                           // 8
+    Qt::MetaModifier | Qt::ShiftModifier,                       // 9
+    Qt::MetaModifier | Qt::ControlModifier,                    // 10
+    Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier,// 11
+    Qt::MetaModifier | Qt::AltModifier,                        // 12
+    Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier,    // 13
+    Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier,  // 14
+    Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier,  // 15
+};
+
+/* key maps */
+struct qt_mac_enum_mapper
+{
+    int mac_code;
+    int qt_code;
+#if defined(DEBUG_KEY_BINDINGS)
+#   define QT_MAC_MAP_ENUM(x) x, #x
+    const char *desc;
+#else
+#   define QT_MAC_MAP_ENUM(x) x
+#endif
+};
+
+//modifiers
+static qt_mac_enum_mapper qt_mac_modifier_symbols[] = {
+    { shiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
+    { rightShiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
+    { controlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
+    { rightControlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
+    { cmdKey, QT_MAC_MAP_ENUM(Qt::ControlModifier) },
+    { optionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
+    { rightOptionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
+    { kEventKeyModifierNumLockMask, QT_MAC_MAP_ENUM(Qt::KeypadModifier) },
+    { 0, QT_MAC_MAP_ENUM(0) }
+};
+Qt::KeyboardModifiers qt_mac_get_modifiers(int keys)
+{
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+    qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", keys, keys);
+#endif
+    Qt::KeyboardModifiers ret = Qt::NoModifier;
+    for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
+        if (keys & qt_mac_modifier_symbols[i].mac_code) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+            qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
+#endif
+            ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code);
+        }
+    }
+    if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+        Qt::KeyboardModifiers oldModifiers = ret;
+        ret &= ~(Qt::MetaModifier | Qt::ControlModifier);
+        if (oldModifiers & Qt::ControlModifier)
+            ret |= Qt::MetaModifier;
+        if (oldModifiers & Qt::MetaModifier)
+            ret |= Qt::ControlModifier;
+    }
+    return ret;
+}
+static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys)
+{
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+    qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", (int)keys, (int)keys);
+#endif
+    int ret = 0;
+    for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
+        if (keys & qt_mac_modifier_symbols[i].qt_code) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+            qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
+#endif
+            ret |= qt_mac_modifier_symbols[i].mac_code;
+        }
+    }
+
+    if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+        int oldModifiers = ret;
+        ret &= ~(controlKeyBit | cmdKeyBit);
+        if (oldModifiers & controlKeyBit)
+            ret |= cmdKeyBit;
+        if (oldModifiers & cmdKeyBit)
+            ret |= controlKeyBit;
+    }
+    return ret;
+}
+void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object)
+{
+    static quint32 cachedModifiers = 0;
+    quint32 lastModifiers = cachedModifiers,
+          changedModifiers = lastModifiers ^ modifiers;
+    cachedModifiers = modifiers;
+
+    //check the bits
+    static qt_mac_enum_mapper modifier_key_symbols[] = {
+        { shiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) },
+        { rightShiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) }, //???
+        { controlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) },
+        { rightControlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) }, //???
+        { cmdKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Control) },
+        { optionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) },
+        { rightOptionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) }, //???
+        { alphaLockBit, QT_MAC_MAP_ENUM(Qt::Key_CapsLock) },
+        { kEventKeyModifierNumLockBit, QT_MAC_MAP_ENUM(Qt::Key_NumLock) },
+        {   0, QT_MAC_MAP_ENUM(0) } };
+    for (int i = 0; i <= 32; i++) { //just check each bit
+        if (!(changedModifiers & (1 << i)))
+            continue;
+        QEvent::Type etype = QEvent::KeyPress;
+        if (lastModifiers & (1 << i))
+            etype = QEvent::KeyRelease;
+        int key = 0;
+        for (uint x = 0; modifier_key_symbols[x].mac_code; x++) {
+            if (modifier_key_symbols[x].mac_code == i) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+                qDebug("got modifier changed: %s", modifier_key_symbols[x].desc);
+#endif
+                key = modifier_key_symbols[x].qt_code;
+                break;
+            }
+        }
+        if (!key) {
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+            qDebug("could not get modifier changed: %d", i);
+#endif
+            continue;
+        }
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+        qDebug("KeyEvent (modif): Sending %s to %s::%s: %d - 0x%08x",
+               etype == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
+               object ? object->metaObject()->className() : "none",
+               object ? object->objectName().toLatin1().constData() : "",
+               key, (int)modifiers);
+#endif
+        QKeyEvent ke(etype, key, qt_mac_get_modifiers(modifiers ^ (1 << i)), QLatin1String(""));
+        qt_sendSpontaneousEvent(object, &ke);
+    }
+}
+
+//keyboard keys (non-modifiers)
+static qt_mac_enum_mapper qt_mac_keyboard_symbols[] = {
+    { kHomeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Home) },
+    { kEnterCharCode, QT_MAC_MAP_ENUM(Qt::Key_Enter) },
+    { kEndCharCode, QT_MAC_MAP_ENUM(Qt::Key_End) },
+    { kBackspaceCharCode, QT_MAC_MAP_ENUM(Qt::Key_Backspace) },
+    { kTabCharCode, QT_MAC_MAP_ENUM(Qt::Key_Tab) },
+    { kPageUpCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageUp) },
+    { kPageDownCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageDown) },
+    { kReturnCharCode, QT_MAC_MAP_ENUM(Qt::Key_Return) },
+    { kEscapeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Escape) },
+    { kLeftArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Left) },
+    { kRightArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Right) },
+    { kUpArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Up) },
+    { kDownArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Down) },
+    { kHelpCharCode, QT_MAC_MAP_ENUM(Qt::Key_Help) },
+    { kDeleteCharCode, QT_MAC_MAP_ENUM(Qt::Key_Delete) },
+//ascii maps, for debug
+    { ':', QT_MAC_MAP_ENUM(Qt::Key_Colon) },
+    { ';', QT_MAC_MAP_ENUM(Qt::Key_Semicolon) },
+    { '<', QT_MAC_MAP_ENUM(Qt::Key_Less) },
+    { '=', QT_MAC_MAP_ENUM(Qt::Key_Equal) },
+    { '>', QT_MAC_MAP_ENUM(Qt::Key_Greater) },
+    { '?', QT_MAC_MAP_ENUM(Qt::Key_Question) },
+    { '@', QT_MAC_MAP_ENUM(Qt::Key_At) },
+    { ' ', QT_MAC_MAP_ENUM(Qt::Key_Space) },
+    { '!', QT_MAC_MAP_ENUM(Qt::Key_Exclam) },
+    { '"', QT_MAC_MAP_ENUM(Qt::Key_QuoteDbl) },
+    { '#', QT_MAC_MAP_ENUM(Qt::Key_NumberSign) },
+    { '$', QT_MAC_MAP_ENUM(Qt::Key_Dollar) },
+    { '%', QT_MAC_MAP_ENUM(Qt::Key_Percent) },
+    { '&', QT_MAC_MAP_ENUM(Qt::Key_Ampersand) },
+    { '\'', QT_MAC_MAP_ENUM(Qt::Key_Apostrophe) },
+    { '(', QT_MAC_MAP_ENUM(Qt::Key_ParenLeft) },
+    { ')', QT_MAC_MAP_ENUM(Qt::Key_ParenRight) },
+    { '*', QT_MAC_MAP_ENUM(Qt::Key_Asterisk) },
+    { '+', QT_MAC_MAP_ENUM(Qt::Key_Plus) },
+    { ',', QT_MAC_MAP_ENUM(Qt::Key_Comma) },
+    { '-', QT_MAC_MAP_ENUM(Qt::Key_Minus) },
+    { '.', QT_MAC_MAP_ENUM(Qt::Key_Period) },
+    { '/', QT_MAC_MAP_ENUM(Qt::Key_Slash) },
+    { '[', QT_MAC_MAP_ENUM(Qt::Key_BracketLeft) },
+    { ']', QT_MAC_MAP_ENUM(Qt::Key_BracketRight) },
+    { '\\', QT_MAC_MAP_ENUM(Qt::Key_Backslash) },
+    { '_', QT_MAC_MAP_ENUM(Qt::Key_Underscore) },
+    { '`', QT_MAC_MAP_ENUM(Qt::Key_QuoteLeft) },
+    { '{', QT_MAC_MAP_ENUM(Qt::Key_BraceLeft) },
+    { '}', QT_MAC_MAP_ENUM(Qt::Key_BraceRight) },
+    { '|', QT_MAC_MAP_ENUM(Qt::Key_Bar) },
+    { '~', QT_MAC_MAP_ENUM(Qt::Key_AsciiTilde) },
+    { '^', QT_MAC_MAP_ENUM(Qt::Key_AsciiCircum) },
+    {   0, QT_MAC_MAP_ENUM(0) }
+};
+
+static qt_mac_enum_mapper qt_mac_keyvkey_symbols[] = { //real scan codes
+    { 122, QT_MAC_MAP_ENUM(Qt::Key_F1) },
+    { 120, QT_MAC_MAP_ENUM(Qt::Key_F2) },
+    { 99,  QT_MAC_MAP_ENUM(Qt::Key_F3) },
+    { 118, QT_MAC_MAP_ENUM(Qt::Key_F4) },
+    { 96,  QT_MAC_MAP_ENUM(Qt::Key_F5) },
+    { 97,  QT_MAC_MAP_ENUM(Qt::Key_F6) },
+    { 98,  QT_MAC_MAP_ENUM(Qt::Key_F7) },
+    { 100, QT_MAC_MAP_ENUM(Qt::Key_F8) },
+    { 101, QT_MAC_MAP_ENUM(Qt::Key_F9) },
+    { 109, QT_MAC_MAP_ENUM(Qt::Key_F10) },
+    { 103, QT_MAC_MAP_ENUM(Qt::Key_F11) },
+    { 111, QT_MAC_MAP_ENUM(Qt::Key_F12) },
+    { 105, QT_MAC_MAP_ENUM(Qt::Key_F13) },
+    { 107, QT_MAC_MAP_ENUM(Qt::Key_F14) },
+    { 113, QT_MAC_MAP_ENUM(Qt::Key_F15) },
+    { 106, QT_MAC_MAP_ENUM(Qt::Key_F16) },
+    {   0, QT_MAC_MAP_ENUM(0) }
+};
+
+static int qt_mac_get_key(int modif, const QChar &key, int virtualKey)
+{
+#ifdef DEBUG_KEY_BINDINGS
+    qDebug("**Mapping key: %d (0x%04x) - %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey);
+#endif
+
+    if (key == kClearCharCode && virtualKey == 0x47)
+        return Qt::Key_Clear;
+
+    if (key.isDigit()) {
+#ifdef DEBUG_KEY_BINDINGS
+            qDebug("%d: got key: %d", __LINE__, key.digitValue());
+#endif
+        return key.digitValue() + Qt::Key_0;
+    }
+
+    if (key.isLetter()) {
+#ifdef DEBUG_KEY_BINDINGS
+        qDebug("%d: got key: %d", __LINE__, (key.toUpper().unicode() - 'A'));
+#endif
+        return (key.toUpper().unicode() - 'A') + Qt::Key_A;
+    }
+    if (key.isSymbol()) {
+#ifdef DEBUG_KEY_BINDINGS
+        qDebug("%d: got key: %d", __LINE__, (key.unicode()));
+#endif
+        return key.unicode();
+    }
+
+    for (int i = 0; qt_mac_keyboard_symbols[i].qt_code; i++) {
+        if (qt_mac_keyboard_symbols[i].mac_code == key) {
+            /* To work like Qt for X11 we issue Backtab when Shift + Tab are pressed */
+            if (qt_mac_keyboard_symbols[i].qt_code == Qt::Key_Tab && (modif & Qt::ShiftModifier)) {
+#ifdef DEBUG_KEY_BINDINGS
+                qDebug("%d: got key: Qt::Key_Backtab", __LINE__);
+#endif
+                return Qt::Key_Backtab;
+            }
+
+#ifdef DEBUG_KEY_BINDINGS
+            qDebug("%d: got key: %s", __LINE__, qt_mac_keyboard_symbols[i].desc);
+#endif
+            return qt_mac_keyboard_symbols[i].qt_code;
+        }
+    }
+
+    //last ditch try to match the scan code
+    for (int i = 0; qt_mac_keyvkey_symbols[i].qt_code; i++) {
+        if (qt_mac_keyvkey_symbols[i].mac_code == virtualKey) {
+#ifdef DEBUG_KEY_BINDINGS
+            qDebug("%d: got key: %s", __LINE__, qt_mac_keyvkey_symbols[i].desc);
+#endif
+            return qt_mac_keyvkey_symbols[i].qt_code;
+        }
+    }
+
+    //oh well
+#ifdef DEBUG_KEY_BINDINGS
+    qDebug("Unknown case.. %s:%d %d[%d] %d", __FILE__, __LINE__, key.unicode(), key.toLatin1(), virtualKey);
+#endif
+    return Qt::Key_unknown;
+}
+
+static Boolean qt_KeyEventComparatorProc(EventRef inEvent, void *data)
+{
+    UInt32 ekind = GetEventKind(inEvent),
+           eclass = GetEventClass(inEvent);
+    return (eclass == kEventClassKeyboard && (void *)ekind == data);
+}
+
+static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, int *qtKey,
+                                      QChar *outChar, Qt::KeyboardModifiers *outModifiers, bool *outHandled)
+{
+#if !defined(QT_MAC_USE_COCOA) || defined(Q_OS_MAC64)
+    Q_UNUSED(er);
+    Q_UNUSED(outHandled);
+#endif
+    const UInt32 ekind = GetEventKind(keyEvent);
+    {
+        UInt32 mac_modifiers = 0;
+        GetEventParameter(keyEvent, kEventParamKeyModifiers, typeUInt32, 0,
+                          sizeof(mac_modifiers), 0, &mac_modifiers);
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+        qDebug("************ Mapping modifiers and key ***********");
+#endif
+        *outModifiers = qt_mac_get_modifiers(mac_modifiers);
+#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
+        qDebug("------------ Mapping modifiers and key -----------");
+#endif
+    }
+
+    //get keycode
+    UInt32 keyCode = 0;
+    GetEventParameter(keyEvent, kEventParamKeyCode, typeUInt32, 0, sizeof(keyCode), 0, &keyCode);
+
+    //get mac mapping
+    static UInt32 tmp_unused_state = 0L;
+    const UCKeyboardLayout *uchrData = 0;
+#if defined(Q_OS_MAC32)
+    KeyboardLayoutRef keyLayoutRef = 0;
+    KLGetCurrentKeyboardLayout(&keyLayoutRef);
+    OSStatus err;
+    if (keyLayoutRef != 0) {
+        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
+                                  (reinterpret_cast<const void **>(&uchrData)));
+        if (err != noErr) {
+            qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d",
+                     long(err), __FILE__, __LINE__);
+        }
+    }
+#else
+    QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource();
+    Q_ASSERT(inputSource != 0);
+    CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource,
+                                                                 kTISPropertyUnicodeKeyLayoutData));
+    uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
+#endif
+    *qtKey = Qt::Key_unknown;
+    if (uchrData) {
+        // The easy stuff; use the unicode stuff!
+        UniChar string[4];
+        UniCharCount actualLength;
+        UInt32 currentModifiers = GetCurrentEventKeyModifiers();
+        UInt32 currentModifiersWOAltOrControl = currentModifiers & ~(controlKey | optionKey);
+        int keyAction;
+        switch (ekind) {
+        default:
+        case kEventRawKeyDown:
+            keyAction = kUCKeyActionDown;
+            break;
+        case kEventRawKeyUp:
+            keyAction = kUCKeyActionUp;
+            break;
+        case kEventRawKeyRepeat:
+            keyAction = kUCKeyActionAutoKey;
+            break;
+        }
+        OSStatus err = UCKeyTranslate(uchrData, keyCode, keyAction,
+                                  ((currentModifiersWOAltOrControl >> 8) & 0xff), LMGetKbdType(),
+                                  kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
+                                  string);
+        if (err == noErr) {
+            *outChar = QChar(string[0]);
+            *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
+            if (currentModifiersWOAltOrControl != currentModifiers) {
+                // Now get the real char.
+                err = UCKeyTranslate(uchrData, keyCode, keyAction,
+                                     ((currentModifiers >> 8) & 0xff), LMGetKbdType(),
+                                      kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
+                                      string);
+                if (err == noErr)
+                    *outChar = QChar(string[0]);
+            }
+        } else {
+            qWarning("Qt::internal::UCKeyTranslate is returnining %ld %s:%d",
+                     long(err), __FILE__, __LINE__);
+        }
+    }
+#ifdef Q_OS_MAC32
+    else {
+        // The road less travelled; use KeyTranslate
+        const void *keyboard_layout;
+        KeyboardLayoutRef keyLayoutRef = 0;
+        KLGetCurrentKeyboardLayout(&keyLayoutRef);
+        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
+                                  reinterpret_cast<const void **>(&keyboard_layout));
+
+        int translatedChar = KeyTranslate(keyboard_layout, (GetCurrentEventKeyModifiers() &
+                                                             (kEventKeyModifierNumLockMask|shiftKey|cmdKey|
+                                                              rightShiftKey|alphaLock)) | keyCode,
+                                           &tmp_unused_state);
+        if (!translatedChar) {
+#ifdef QT_MAC_USE_COCOA
+            if (outHandled) {
+                qt_mac_eat_unicode_key = false;
+                if (er)
+                    CallNextEventHandler(er, keyEvent);
+                *outHandled = qt_mac_eat_unicode_key;
+            }
+#endif
+            return false;
+        }
+
+        //map it into qt keys
+        *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
+        if (*outModifiers & (Qt::AltModifier | Qt::ControlModifier)) {
+            if (translatedChar & (1 << 7)) //high ascii
+                translatedChar = 0;
+        } else {          //now get the real ascii value
+            UInt32 tmp_mod = 0L;
+            static UInt32 tmp_state = 0L;
+            if (*outModifiers & Qt::ShiftModifier)
+                tmp_mod |= shiftKey;
+            if (*outModifiers & Qt::MetaModifier)
+                tmp_mod |= controlKey;
+            if (*outModifiers & Qt::ControlModifier)
+                tmp_mod |= cmdKey;
+            if (GetCurrentEventKeyModifiers() & alphaLock) //no Qt mapper
+                tmp_mod |= alphaLock;
+            if (*outModifiers & Qt::AltModifier)
+                tmp_mod |= optionKey;
+            if (*outModifiers & Qt::KeypadModifier)
+                tmp_mod |= kEventKeyModifierNumLockMask;
+            translatedChar = KeyTranslate(keyboard_layout, tmp_mod | keyCode, &tmp_state);
+        }
+        {
+            ByteCount unilen = 0;
+            if (GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0)
+                    == noErr && unilen == 2) {
+                GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, outChar);
+            } else if (translatedChar) {
+                static QTextCodec *c = 0;
+                if (!c)
+                    c = QTextCodec::codecForName("Apple Roman");
+		char tmpChar = (char)translatedChar; // **sigh**
+                *outChar = c->toUnicode(&tmpChar, 1).at(0);
+            } else {
+                *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
+            }
+        }
+    }
+#endif
+    if (*qtKey == Qt::Key_unknown)
+        *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
+    return true;
+}
+
+QKeyMapperPrivate::QKeyMapperPrivate()
+{
+    memset(keyLayout, 0, sizeof(keyLayout));
+    keyboard_layout_format.unicode = 0;
+#ifdef Q_OS_MAC32
+    keyboard_mode = NullMode;
+#else
+    currentInputSource = 0;
+#endif
+}
+
+QKeyMapperPrivate::~QKeyMapperPrivate()
+{
+    deleteLayouts();
+}
+
+bool
+QKeyMapperPrivate::updateKeyboard()
+{
+    const UCKeyboardLayout *uchrData = 0;
+#ifdef Q_OS_MAC32
+    KeyboardLayoutRef keyLayoutRef = 0;
+    KLGetCurrentKeyboardLayout(&keyLayoutRef);
+
+    if (keyboard_mode != NullMode && currentKeyboardLayout == keyLayoutRef)
+        return false;
+
+    OSStatus err;
+    if (keyLayoutRef != 0) {
+        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
+                                  const_cast<const void **>(reinterpret_cast<const void **>(&uchrData)));
+        if (err != noErr) {
+            qWarning("Qt::internal::unable to get unicode keyboardlayout %ld %s:%d",
+                     long(err), __FILE__, __LINE__);
+        }
+    }
+#else
+    QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
+    if (keyboard_mode != NullMode && source == currentInputSource) {
+        return false;
+    }
+    Q_ASSERT(source != 0);
+    CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(source,
+                                                                 kTISPropertyUnicodeKeyLayoutData));
+    uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
+#endif
+
+    keyboard_kind = LMGetKbdType();
+    if (uchrData) {
+        keyboard_layout_format.unicode = uchrData;
+        keyboard_mode = UnicodeMode;
+    }
+#ifdef Q_OS_MAC32
+    else {
+        void *happy;
+        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
+                                  const_cast<const void **>(reinterpret_cast<void **>(&happy)));
+        if (err != noErr) {
+            qFatal("Qt::internal::unable to get non-unicode layout, cannot procede %ld %s:%d",
+                     long(err), __FILE__, __LINE__);
+        }
+        keyboard_layout_format.other = happy;
+        keyboard_mode = OtherMode;
+    }
+
+    currentKeyboardLayout = keyLayoutRef;
+#else
+    currentInputSource = source;
+#endif
+    keyboard_dead = 0;
+    CFStringRef iso639Code;
+#ifdef Q_OS_MAC32
+# ifndef kKLLanguageCode
+# define kKLLanguageCode 9
+# endif
+    KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLLanguageCode,
+                                reinterpret_cast<const void **>(&iso639Code));
+#else
+    CFArrayRef array = static_cast<CFArrayRef>(TISGetInputSourceProperty(currentInputSource, kTISPropertyInputSourceLanguages));
+    iso639Code = static_cast<CFStringRef>(CFArrayGetValueAtIndex(array, 0)); // Actually a RFC3066bis, but it's close enough
+#endif
+    if (iso639Code) {
+        keyboardInputLocale = QLocale(QCFString::toQString(iso639Code));
+        QString monday = keyboardInputLocale.dayName(1);
+        bool rtl = false;
+        for (int i = 0; i < monday.length(); ++i) {
+            switch (monday.at(i).direction()) {
+            default:
+                break;
+            case QChar::DirR:
+            case QChar::DirAL:
+            case QChar::DirRLE:
+            case QChar::DirRLO:
+                rtl = true;
+                break;
+            }
+            if (rtl)
+                break;
+        }
+        keyboardInputDirection = rtl ? Qt::RightToLeft : Qt::LeftToRight;
+    } else {
+        keyboardInputLocale = QLocale::c();
+        keyboardInputDirection = Qt::LeftToRight;
+    }
+    return true;
+}
+
+void
+QKeyMapperPrivate::deleteLayouts()
+{
+    keyboard_mode = NullMode;
+    for (int i = 0; i < 255; ++i) {
+        if (keyLayout[i]) {
+            delete keyLayout[i];
+            keyLayout[i] = 0;
+        }
+    }
+}
+
+void
+QKeyMapperPrivate::clearMappings()
+{
+    deleteLayouts();
+    updateKeyboard();
+}
+
+QList<int>
+QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
+{
+    QList<int> ret;
+
+    KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()];
+    if (!kbItem) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard) 
+        return ret;
+
+    int baseKey = kbItem->qtKey[0];
+    Qt::KeyboardModifiers keyMods = e->modifiers();
+    ret << int(baseKey + keyMods); // The base key is _always_ valid, of course
+
+    for (int i = 1; i < 8; ++i) {
+        Qt::KeyboardModifiers neededMods = ModsTbl[i];
+        int key = kbItem->qtKey[i];
+        if (key && key != baseKey && ((keyMods & neededMods) == neededMods))
+            ret << int(key + (keyMods & ~neededMods));
+    }
+
+    return ret;
+}
+
+bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef er, EventRef event,
+                                          void *info, bool grab)
+{
+    Q_ASSERT(GetEventClass(event) == kEventClassKeyboard);
+    bool handled_event=true;
+    UInt32 ekind = GetEventKind(event);
+
+    // unfortunately modifiers changed event looks quite different, so I have a separate
+    // code path
+    if (ekind == kEventRawKeyModifiersChanged) {
+        //figure out changed modifiers, wish Apple would just send a delta
+        UInt32 modifiers = 0;
+        GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+                          sizeof(modifiers), 0, &modifiers);
+        qt_mac_send_modifiers_changed(modifiers, widget);
+        return true;
+    }
+
+    if (qApp->inputContext() && qApp->inputContext()->isComposing()) {
+        if (ekind == kEventRawKeyDown) {
+            QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext());
+            if (context)
+                context->setLastKeydownEvent(event);
+        }
+        return false;
+    }
+    //get modifiers
+    Qt::KeyboardModifiers modifiers;
+    int qtKey;
+    QChar ourChar;
+    if (translateKeyEventInternal(er, event, &qtKey, &ourChar, &modifiers,
+                                  &handled_event) == false)
+        return handled_event;
+    QString text(ourChar);
+    /* This is actually wrong - but unfortunatly it is the best that can be
+       done for now because of the Control/Meta mapping problems */
+    if (modifiers & (Qt::ControlModifier | Qt::MetaModifier)
+        && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
+        text = QString();
+    }
+
+
+    if (widget) {
+#ifndef QT_MAC_USE_COCOA
+        Q_UNUSED(info);
+        // Try not to call "other" event handlers if we have a popup,
+        // However, if the key has text
+        // then we should pass it along because otherwise then people
+        // can use input method stuff.
+        if (!qApp->activePopupWidget()
+                || (qApp->activePopupWidget() && !text.isEmpty())) {
+            //Find out if someone else wants the event, namely
+            //is it of use to text services? If so we won't bother
+            //with a QKeyEvent.
+            qt_mac_eat_unicode_key = false;
+            if (er)
+                CallNextEventHandler(er, event);
+            extern bool qt_mac_menubar_is_open();   
+            if (qt_mac_eat_unicode_key || qt_mac_menubar_is_open()) {
+                return true;
+            }
+        }
+#endif
+        // Try to compress key events.
+        if (!text.isEmpty() && widget->testAttribute(Qt::WA_KeyCompression)) {
+            EventTime lastTime = GetEventTime(event);
+            for (;;) {
+                EventRef releaseEvent = FindSpecificEventInQueue(GetMainEventQueue(),
+                                                                 qt_KeyEventComparatorProc,
+                                                                 (void*)kEventRawKeyUp);
+                if (!releaseEvent)
+                    break;
+                const EventTime releaseTime = GetEventTime(releaseEvent);
+                if (releaseTime < lastTime)
+                    break;
+                lastTime = releaseTime;
+
+                EventRef pressEvent = FindSpecificEventInQueue(GetMainEventQueue(),
+                                                               qt_KeyEventComparatorProc,
+                                                               (void*)kEventRawKeyDown);
+                if (!pressEvent)
+                    break;
+                const EventTime pressTime = GetEventTime(pressEvent);
+                if (pressTime < lastTime)
+                    break;
+                lastTime = pressTime;
+
+                Qt::KeyboardModifiers compressMod;
+                int compressQtKey = 0;
+                QChar compressChar;
+                if (translateKeyEventInternal(er, pressEvent,
+                                              &compressQtKey, &compressChar, &compressMod, 0)
+                    == false) {
+                    break;
+                }
+                // Copied from qapplication_x11.cpp (change both).
+
+                bool stopCompression =
+                    // 1) misc keys
+                    (compressQtKey >= Qt::Key_Escape && compressQtKey <= Qt::Key_SysReq)
+                    // 2) cursor movement
+                    || (compressQtKey >= Qt::Key_Home && compressQtKey <= Qt::Key_PageDown)
+                    // 3) extra keys
+                    || (compressQtKey >= Qt::Key_Super_L && compressQtKey <= Qt::Key_Direction_R)
+                    // 4) something that a) doesn't translate to text or b) translates
+                    //    to newline text
+                    || (compressQtKey == 0)
+                    || (compressChar == QLatin1Char('\n'))
+                    || (compressQtKey == Qt::Key_unknown);
+
+                if (compressMod == modifiers && !compressChar.isNull() && !stopCompression) {
+#ifdef DEBUG_KEY_BINDINGS
+                    qDebug("compressing away %c", compressChar.toLatin1());
+#endif
+                    text += compressChar;
+                    // Clean up
+                    RemoveEventFromQueue(GetMainEventQueue(), releaseEvent);
+                    RemoveEventFromQueue(GetMainEventQueue(), pressEvent);
+                } else {
+#ifdef DEBUG_KEY_BINDINGS
+                    qDebug("stoping compression..");
+#endif
+                    break;
+                }
+            }
+        }
+
+        // There is no way to get the scan code from carbon. But we cannot use the value 0, since
+        // it indicates that the event originates from somewhere else than the keyboard
+        UInt32 macScanCode = 1;
+        UInt32 macVirtualKey = 0;
+        GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
+        UInt32 macModifiers = 0;
+        GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
+                          sizeof(macModifiers), 0, &macModifiers);
+        handled_event = QKeyMapper::sendKeyEvent(widget, grab,
+                                                 (ekind == kEventRawKeyUp) ? QEvent::KeyRelease : QEvent::KeyPress,
+                                                 qtKey, modifiers, text, ekind == kEventRawKeyRepeat, 0,
+                                                 macScanCode, macVirtualKey, macModifiers
+#ifdef QT_MAC_USE_COCOA
+                                                 ,static_cast<bool *>(info)
+#endif
+                                                 );
+    }
+    return handled_event;
+}
+
+void
+QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void *)
+{
+    UInt32 macVirtualKey = 0;
+    GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
+    if (updateKeyboard())
+       QKeyMapper::changeKeyboard();
+    else if (keyLayout[macVirtualKey])
+        return;
+
+    UniCharCount buffer_size = 10;
+    UniChar buffer[buffer_size];
+    keyLayout[macVirtualKey] = new KeyboardLayoutItem;
+    for (int i = 0; i < 16; ++i) {
+        UniCharCount out_buffer_size = 0;
+        keyLayout[macVirtualKey]->qtKey[i] = 0;
+#ifdef Q_WS_MAC32
+        if (keyboard_mode == UnicodeMode) {
+#endif
+            const UInt32 keyModifier = ((qt_mac_get_mac_modifiers(ModsTbl[i]) >> 8) & 0xFF);
+            OSStatus err = UCKeyTranslate(keyboard_layout_format.unicode, macVirtualKey, kUCKeyActionDown, keyModifier,
+                                          keyboard_kind, 0, &keyboard_dead, buffer_size, &out_buffer_size, buffer);
+            if (err == noErr && out_buffer_size) {
+                const QChar unicode(buffer[0]);
+                int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
+                if (qtkey == Qt::Key_unknown)
+                    qtkey = unicode.unicode();
+                keyLayout[macVirtualKey]->qtKey[i] = qtkey;
+            }
+#ifdef Q_WS_MAC32            
+        } else {
+            const UInt32 keyModifier = (qt_mac_get_mac_modifiers(ModsTbl[i]));
+
+            uchar translatedChar = KeyTranslate(keyboard_layout_format.other, keyModifier | macVirtualKey, &keyboard_dead);
+            if (translatedChar) {
+                static QTextCodec *c = 0;
+                if (!c)
+                    c = QTextCodec::codecForName("Apple Roman");
+                const QChar unicode(c->toUnicode((const char *)&translatedChar, 1).at(0));
+                int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
+                if (qtkey == Qt::Key_unknown)
+                    qtkey = unicode.unicode();
+                keyLayout[macVirtualKey]->qtKey[i] = qtkey;
+            }
+        }
+#endif
+    }
+#ifdef DEBUG_KEY_MAPS
+    qDebug("updateKeyMap for virtual key = 0x%02x!", (uint)macVirtualKey);
+    for (int i = 0; i < 16; ++i) {
+        qDebug("    [%d] (%d,0x%02x,'%c')", i,
+               keyLayout[macVirtualKey]->qtKey[i],
+               keyLayout[macVirtualKey]->qtKey[i],
+               keyLayout[macVirtualKey]->qtKey[i]);
+    }
+#endif
+}
+
+bool
+QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
+                         QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
+                         const QString &text, bool autorepeat, int count,
+                         quint32 nativeScanCode, quint32 nativeVirtualKey,
+                         quint32 nativeModifiers, bool *isAccepted)
+{
+    Q_UNUSED(count);
+    if (widget && widget->isEnabled()) {
+        bool key_event = true;
+#if defined(QT3_SUPPORT) && !defined(QT_NO_SHORTCUT)
+        if (type == QEvent::KeyPress && !grab
+           && QApplicationPrivate::instance()->use_compat()) {
+               QKeyEventEx accel_ev(type, code, modifiers,
+                                    text, autorepeat, qMax(1, int(text.length())),
+                                    nativeScanCode, nativeVirtualKey, nativeModifiers);
+            if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &accel_ev)) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+                qDebug("KeyEvent: %s::%s consumed Accel: %s",
+                       widget ? widget->metaObject()->className() : "none",
+                       widget ? widget->objectName().toLatin1().constData() : "",
+                       text.toLatin1().constData());
+#endif
+                key_event = false;
+            } else {
+                if (accel_ev.isAccepted()) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+                    qDebug("KeyEvent: %s::%s overrode Accel: %s",
+                           widget ? widget->metaObject()->className() : "none",
+                           widget ? widget->objectName().toLatin1().constData() : "",
+                           text.toLatin1().constData());
+#endif
+                }
+            }
+        }
+#else
+Q_UNUSED(grab);
+#endif // QT3_SUPPORT && !QT_NO_SHORTCUT
+        if (key_event) {
+#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
+            qDebug("KeyEvent: Sending %s to %s::%s: %s 0x%08x%s",
+                   type == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
+                   widget ? widget->metaObject()->className() : "none",
+                   widget ? widget->objectName().toLatin1().constData() : "",
+                   text.toLatin1().constData(), int(modifiers),
+                   autorepeat ? " Repeat" : "");
+#endif
+            QKeyEventEx ke(type, code, modifiers, text, autorepeat, qMax(1, text.length()),
+                           nativeScanCode, nativeVirtualKey, nativeModifiers);
+            bool retMe = qt_sendSpontaneousEvent(widget,&ke);
+            if (isAccepted)
+                *isAccepted = ke.isAccepted();
+            return retMe;
+        }
+    }
+    return false;
+}
+
+QT_END_NAMESPACE