/**************************************************************************** ** ** 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