/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** Copyright (C) 2009 netlabs.org. OS/2 parts. ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** 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.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qkeymapper_p.h" #include "qapplication_p.h" #include "qevent_p.h" #include "qwidget.h" #include "qt_os2.h" QT_BEGIN_NAMESPACE //#define DEBUG_KEYMAPPER // Key recorder ----------------------------------------------------[ start ] -- struct KeyRecord { KeyRecord(int _scan, int _code, int _state, const QString &_text) : scan(_scan), code(_code), state(_state), text(_text) {} KeyRecord() {} int scan; int code; int state; QString text; }; static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers... struct KeyRecorder { KeyRecorder() : nrecs(0) {} inline KeyRecord *findKey(int code, bool remove); inline void storeKey(int scan, int code, int state, const QString& text); inline void clearKeys(); int nrecs; KeyRecord deleted_record; // A copy of last entry removed from records[] KeyRecord records[QT_MAX_KEY_RECORDINGS]; }; static KeyRecorder key_recorder; KeyRecord *KeyRecorder::findKey(int scan, bool remove) { KeyRecord *result = 0; for (int i = 0; i < nrecs; ++i) { if (records[i].scan == scan) { if (remove) { deleted_record = records[i]; // Move rest down, and decrease count while (i + 1 < nrecs) { records[i] = records[i + 1]; ++i; } --nrecs; result = &deleted_record; } else { result = &records[i]; } break; } } return result; } void KeyRecorder::storeKey(int scan, int code, int state, const QString& text) { Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS, "Internal KeyRecorder", "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS"); if (nrecs == QT_MAX_KEY_RECORDINGS) { qWarning("Qt: Internal keyboard buffer overflow"); return; } records[nrecs++] = KeyRecord(scan, code, state, text); } void KeyRecorder::clearKeys() { nrecs = 0; } // Key recorder ------------------------------------------------------[ end ] -- // Key translation -------------------------------------------------[ start ] -- // Meaning of values: // 0 = Character output key, needs keyboard driver mapping // Key_unknown = Unknown Virtual Key, no translation possible, ignore static const uint KeyTbl[] = { // Keyboard mapping table // Dec | Hex | PM Virtual key Qt::Key_unknown, // 0 0x00 Qt::Key_unknown, // 1 0x01 VK_BUTTON1 | Mouse button 1 Qt::Key_unknown, // 2 0x02 VK_BUTTON2 | Mouse button 2 Qt::Key_unknown, // 3 0x03 VK_BUTTON3 | Mouse button 3 Qt::Key_Cancel, // 4 0x04 VK_BREAK | Control-Break processing Qt::Key_Backspace, // 5 0x05 VK_BACKSPACE | BackSpace key Qt::Key_Tab, // 6 0x06 VK_TAB | Tab key Qt::Key_Backtab, // 7 0x07 VK_BACKTAB | Shift+Tab key Qt::Key_Return, // 8 0x08 VK_RETURN | Enter key Qt::Key_Shift, // 9 0x09 VK_SHIFT | Shift key Qt::Key_Control, // 10 0x0A VK_CTRL | Ctrl key Qt::Key_Alt, // 11 0x0B VK_ALT | Alt key Qt::Key_Alt, // 12 0x0C VK_ALTGRAF | AltGr key Qt::Key_Pause, // 13 0x0D VK_PAUSE | Pause key Qt::Key_CapsLock, // 14 0x0E VK_CAPSLOCK | Caps-Lock Qt::Key_Escape, // 15 0x0F VK_ESC | Esc key Qt::Key_Space, // 16 0x10 VK_SPACE | Spacebar Qt::Key_PageUp, // 17 0x11 VK_PAGEUP | Page Up key Qt::Key_PageDown, // 18 0x12 VK_PAGEDOWN | Page Down key Qt::Key_End, // 19 0x13 VK_END | End key Qt::Key_Home, // 20 0x14 VK_HOME | Home key Qt::Key_Left, // 21 0x15 VK_LEFT | Left arrow key Qt::Key_Up, // 22 0x16 VK_UP | Up arrow key Qt::Key_Right, // 23 0x17 VK_RIGHT | Right arrow key Qt::Key_Down, // 24 0x18 VK_DOWN | Down arrow key Qt::Key_Print, // 25 0x19 VK_PRINTSCRN | Print Screen key Qt::Key_Insert, // 26 0x1A VK_INSERT | Ins key Qt::Key_Delete, // 27 0x1B VK_DELETE | Del key Qt::Key_NumLock, // 28 0x1C VK_SCROLL | Scroll Lock key Qt::Key_ScrollLock, // 29 0x1D VK_NUMLOCK | Num Lock key Qt::Key_Enter, // 30 0x1E VK_ENTER | Enter (Numpad) key Qt::Key_SysReq, // 31 0x1F VK_SYSRQ | SysReq key Qt::Key_F1, // 32 0x20 VK_F1 | F1 key Qt::Key_F2, // 33 0x21 VK_F2 | F2 key Qt::Key_F3, // 34 0x22 VK_F3 | F3 key Qt::Key_F4, // 35 0x23 VK_F4 | F4 key Qt::Key_F5, // 36 0x24 VK_F5 | F5 key Qt::Key_F6, // 37 0x25 VK_F6 | F6 key Qt::Key_F7, // 38 0x26 VK_F7 | F7 key Qt::Key_F8, // 39 0x27 VK_F8 | F8 key Qt::Key_F9, // 40 0x28 VK_F9 | F9 key Qt::Key_F10, // 41 0x29 VK_F10 | F10 key Qt::Key_F11, // 42 0x2A VK_F11 | F11 key Qt::Key_F12, // 43 0x2B VK_F12 | F12 key Qt::Key_F13, // 44 0x2C VK_F13 | F13 key Qt::Key_F14, // 45 0x2D VK_F14 | F14 key Qt::Key_F15, // 46 0x2E VK_F15 | F15 key Qt::Key_F16, // 47 0x2F VK_F16 | F16 key Qt::Key_F17, // 48 0x30 VK_F17 | F17 key Qt::Key_F18, // 49 0x31 VK_F18 | F18 key Qt::Key_F19, // 50 0x32 VK_F19 | F19 key Qt::Key_F20, // 51 0x33 VK_F20 | F20 key Qt::Key_F21, // 52 0x34 VK_F21 | F21 key Qt::Key_F22, // 53 0x35 VK_F22 | F22 key Qt::Key_F23, // 54 0x36 VK_F23 | F23 key Qt::Key_F24, // 55 0x37 VK_F24 | F24 key Qt::Key_unknown, // 56 0x38 VK_ENDDRAG | ??? Qt::Key_Clear, // 57 0x39 VK_CLEAR | Clear key Qt::Key_unknown, // 58 0x3A VK_EREOF | ??? Qt::Key_unknown, // 59 0x3B VK_PA1 | ??? Qt::Key_unknown, // 60 0x3C VK_ATTN | ??? Qt::Key_unknown, // 61 0x3D VK_CRSEL | ??? Qt::Key_unknown, // 62 0x3E VK_EXSEL | ??? Qt::Key_unknown, // 63 0x3F VK_COPY | ??? Qt::Key_unknown, // 64 0x40 VK_BLK1 | ??? Qt::Key_unknown, // 65 0x41 VK_BLK2 | ??? }; // Converts known accent symbols to their Key_Dead_ equivalents. static inline int deadCharToDeadKeyCode(const QChar &ch) { switch (ch.unicode()) { case 0x0060: return Qt::Key_Dead_Grave; case 0x00B4: return Qt::Key_Dead_Acute; case 0x005E: return Qt::Key_Dead_Circumflex; case 0x007E: return Qt::Key_Dead_Tilde; case 0x00AF: return Qt::Key_Dead_Macron; case 0x02D8: return Qt::Key_Dead_Breve; case 0x02D9: return Qt::Key_Dead_Abovedot; case 0x00A8: return Qt::Key_Dead_Diaeresis; case 0x02DA: return Qt::Key_Dead_Abovering; case 0x02DD: return Qt::Key_Dead_Doubleacute; case 0x02C7: return Qt::Key_Dead_Caron; case 0x00B8: return Qt::Key_Dead_Cedilla; case 0x02DB: return Qt::Key_Dead_Ogonek; case 0x1DA5: return Qt::Key_Dead_Iota; // @todo is that correct??? case 0x3099: return Qt::Key_Dead_Voiced_Sound; case 0x309A: return Qt::Key_Dead_Semivoiced_Sound; case 0x0323: return Qt::Key_Dead_Belowdot; case 0x02DE: return Qt::Key_Dead_Hook; case 0x031B: return Qt::Key_Dead_Horn; default: break; } // otherwise, return unmodified uppercase unicode value (similar to other // cases) return ch.unicode(); } // Key translation ---------------------------------------------------[ end ]--- // QETWidget class is only for accessing the sendSpontaneousEvent function in QApplication class QETWidget : public QWidget { public: static bool sendSpontaneousEvent(QObject *r, QEvent *e) { return QApplication::sendSpontaneousEvent(r, e); } }; // Keyboard map private --------------------------------------------[ start ]--- QKeyMapperPrivate::QKeyMapperPrivate() { // State holder for LWIN/RWIN and ALTGr keys // (ALTGr is also necessary since OS/2 doesn't report ALTGr as KC_ALT) extraKeyState = 0; // @todo implement } QKeyMapperPrivate::~QKeyMapperPrivate() { clearMappings(); } void QKeyMapperPrivate::clearMappings() { // @todo implement } QList QKeyMapperPrivate::possibleKeys(QKeyEvent *e) { QList result; // @todo implement; so far do the same as QKeyMapper::possibleKeys() if (e->key() && (e->key() != Qt::Key_unknown)) result << int(e->key() + e->modifiers()); else if (!e->text().isEmpty()) result << int(e->text().at(0).unicode() + e->modifiers()); return result; } bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, const QMSG &qmsg, bool grab) { Q_Q(QKeyMapper); bool k0 = false; bool k1 = false; CHRMSG chm = *CHARMSG(&qmsg.msg); // we combine the flags from the message with the raw chr value and pass // them as QKeyEvent::nativeModifiers(). Together with chr.vkey passed as // nativeVirtualKey() and chr.scancode as nativeScanCode(), the Qt // application gets the full key message details (except the repeat count). quint32 nativeMods = chm.fs | chm.chr << 16; // Get the modifier states (may be altered later, depending on key code) int state = 0; if (chm.fs & KC_SHIFT) state |= Qt::ShiftModifier; if (chm.fs & KC_CTRL) state |= Qt::ControlModifier; if (chm.fs & KC_ALT) state |= Qt::AltModifier; // Translate VK_* (native) -> Key_* (Qt) keys int code = 0; if ((chm.fs & KC_VIRTUALKEY)) { if (chm.vkey == 0) { // The only known situation when KC_VIRTUALKEY is present but // vkey is zero is when Alt+Shift is pressed to switch the // keyboard layout state from latin to national and back. // It seems that this way the system informs applications about // layout changes: chm.chr is 0xF1 when the user switches // to the national layout (i.e. presses Alt + Left Shift) // and 0xF0 when he switches back (presses Alt + Right Shift). // We assume this and restore fs, vkey, scancode and chr accordingly. if (chm.chr == 0xF0 || chm.chr == 0xF1) { chm.fs |= KC_ALT | KC_SHIFT; chm.vkey = VK_SHIFT; chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36; chm.chr = 0; state |= Qt::AltModifier | Qt::ShiftModifier; // code will be assigned by the normal procedure below } } else if (chm.vkey == VK_ALTGRAF) { if (!(chm.fs & KC_KEYUP)) extraKeyState |= Qt::AltModifier; else extraKeyState &= ~Qt::AltModifier; } if (chm.vkey < sizeof(KeyTbl)) code = KeyTbl[chm.vkey]; } // detect some special keys that don't have a virtual key but have a pseudo // char code in the high byte of chm.chr (probably this is less // device-dependent than scancode) switch (chm.chr) { case 0xEC00: // LWIN case 0xED00: // RWIN code = Qt::Key_Meta; if (!(chm.fs & KC_KEYUP)) extraKeyState |= Qt::MetaModifier; else extraKeyState &= ~Qt::MetaModifier; break; case 0xEE00: // WINAPP (menu with arrow) code = Qt::Key_Menu; break; case 0x5600: // additional '\' (0x56 is actually its scancode) chm.chr = state & Qt::ShiftModifier ? '|' : '\\'; break; } // update state after updating extraKeyState if (extraKeyState & Qt::AltModifier) state |= Qt::AltModifier; if (extraKeyState & Qt::MetaModifier) state |= Qt::MetaModifier; // Invert state logic: // If the key actually pressed is a modifier key, then we remove its modifier key from the // state, since a modifier-key can't have itself as a modifier // // ### QKeyEvent::modifiers() already does this inversion which in result // cancels the inversion we do below and therefore makes the above statement // incorrect! It looks like the inversion block should be removed from this // function but it is left here for compatibility with other platforms which // also have this bug. // if (code == Qt::Key_Control) state = state ^ Qt::ControlModifier; else if (code == Qt::Key_Shift) state = state ^ Qt::ShiftModifier; else if (code == Qt::Key_Alt) state = state ^ Qt::AltModifier; else if (code == Qt::Key_Meta) state = state ^ Qt::MetaModifier; // KEYDOWN ----------------------------------------------------------------- if (!(chm.fs & KC_KEYUP)) { // Get the last record of this key press, so we can validate the current state // The record is not removed from the list KeyRecord *rec = key_recorder.findKey(chm.scancode, false); // If rec's state doesn't match the current state, something has changed behind our back // (Consumed by modal widget is one possibility) So, remove the record from the list // This will stop the auto-repeat of the key, should a modifier change, for example if (rec && rec->state != state) { key_recorder.findKey(chm.scancode, true); rec = 0; } // If we have a record, it means that the key is already pressed, the state is the same // so, we have an auto-repeating key if (rec) { Q_ASSERT(!code || code == rec->code); if (rec->code < Qt::Key_Shift || rec->code > Qt::Key_ScrollLock) { k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code, Qt::KeyboardModifier(state), rec->text, true, 0, chm.scancode, chm.vkey, nativeMods); k1 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, rec->code, Qt::KeyboardModifier(state), rec->text, true, 0, chm.scancode, chm.vkey, nativeMods); } } // No record of the key being previous pressed, so we now send a QEvent::KeyPress event, // and store the key data into our records. else { QString text; // we assume that we have a correct 8-bit character not only when // KC_CHAR is set but also when there are one or more modifiers // (in which case KC_CHAR is not present) but no KC_VIRTUALKEY. // The latter is in particular important for Alt+Letter combos // processing Alt+Letter shortcuts for non-ASCII letters in widgets // (e.g. QPushButton) depends on that. if (((chm.fs & KC_CHAR) || (!(chm.fs & KC_VIRTUALKEY) && (chm.fs & (KC_CTRL | KC_ALT | KC_SHIFT)))) && chm.chr) { if (chm.chr & 0xFF00) { // We assume we get a DBCS char if the above condition is met. text = QString::fromLocal8Bit((char *)&chm.chr, 2); } else { text = QString::fromLocal8Bit((char*)&chm.chr, 1); } if (chm.fs & KC_DEADKEY) { // convert dead keys to Key_Dead_* codes and set text to // null to avoid interpreting them as normal chars Q_ASSERT(text.size() == 1); code = deadCharToDeadKeyCode(text[0]); text = QString(); } else if (chm.fs & KC_INVALIDCOMP) { // if the pressed letter is invalid for the given dead // key, we set text to null as well (to meet the PM // behavior) and set the code to the uppercase unicode // value (similar to other cases) to have it recognized Q_ASSERT(text.size() == 1); code = text[0].toUpper().unicode(); text = QString(); } Q_ASSERT(code || !text.isEmpty()); // we must provide the key code if (!code && !text.isEmpty()) { code = text[0].toUpper().unicode(); } if ((state & Qt::ControlModifier) && !(state & Qt::KeypadModifier) && !(state & Qt::AltModifier) && !text.isEmpty() && !text[0].row()) { // Ctrl + A..Z etc. produce ascii from 0x01 to 0x1F int ascii = text[0].toUpper().cell(); if (ascii >= 0x41 && ascii <= 0x5F) ascii -= 0x40; // the below emulates OS/2 functionality. It differs from // e.g. Win32. else if (ascii == 0x36 && !(state & Qt::KeypadModifier)) ascii = 0x1E; else if (ascii == 0x2D) ascii = 0x1F; else if (ascii >= 0x7B && ascii <= 0x7D) ascii -= 0x60; text = QChar(ascii); } } key_recorder.storeKey(chm.scancode, code, state, text); k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code, Qt::KeyboardModifier(state), text, false, 0, chm.scancode, chm.vkey, nativeMods); } } // KEYUP ------------------------------------------------------------------- else { // Try to locate the key in our records, and remove it if it exists. // The key may not be in our records if, for example, the down event was handled by // PM natively, or our window gets focus while a key is already press, but now gets // the key release event. KeyRecord* rec = key_recorder.findKey(chm.scancode, true); if (!rec) { // Someone ate the key down event } else { k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code, Qt::KeyboardModifier(state), rec->text, false, 0, chm.scancode, chm.vkey, nativeMods); } } // Return true, if a QKeyEvent was sent to a widget return k0 || k1; } // QKeyMapper (PM) implementation ----------------------------------[ start ]--- 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 *) { Q_UNUSED(count); #if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT) if (type == QEvent::KeyPress && !grab && QApplicationPrivate::instance()->use_compat()) { // send accel events if the keyboard is not grabbed QKeyEventEx a(type, code, modifiers, text, autorepeat, qMax(1, int(text.length())), nativeScanCode, nativeVirtualKey, nativeModifiers); if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &a)) return true; } #endif if (!widget->isEnabled()) return false; QKeyEventEx e(type, code, modifiers, text, autorepeat, qMax(1, int(text.length())), nativeScanCode, nativeVirtualKey, nativeModifiers); QETWidget::sendSpontaneousEvent(widget, &e); return e.isAccepted(); } QT_END_NAMESPACE