Ignore:
Timestamp:
Aug 28, 2009, 7:46:09 PM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: Implemented QKeyMapper (#33).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/kernel/qkeymapper_pm.cpp

    r95 r136  
    4343
    4444#include "qkeymapper_p.h"
     45#include "qapplication_p.h"
     46#include "qevent_p.h"
     47
     48#include "qt_os2.h"
    4549
    4650QT_BEGIN_NAMESPACE
    4751
    48 // Uncommend, to show debugging information for the keymapper
    4952//#define DEBUG_KEYMAPPER
    5053
    51 // Keyboard map private ----------------------------------------------------------------[ start ]---
     54// Key recorder ----------------------------------------------------[ start ] --
     55struct KeyRecord {
     56    KeyRecord(int _scan, int _code, int _state, const QString &_text)
     57        : scan(_scan), code(_code), state(_state), text(_text) {}
     58    KeyRecord() {}
     59
     60    int scan;
     61    int code;
     62    int state;
     63    QString text;
     64};
     65
     66static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers...
     67
     68struct KeyRecorder
     69{
     70    KeyRecorder() : nrecs(0) {}
     71
     72    inline KeyRecord *findKey(int code, bool remove);
     73    inline void storeKey(int scan, int code, int state, const QString& text);
     74    inline void clearKeys();
     75
     76    int nrecs;
     77    KeyRecord deleted_record; // A copy of last entry removed from records[]
     78    KeyRecord records[QT_MAX_KEY_RECORDINGS];
     79};
     80static KeyRecorder key_recorder;
     81
     82KeyRecord *KeyRecorder::findKey(int scan, bool remove)
     83{
     84    KeyRecord *result = 0;
     85    for (int i = 0; i < nrecs; ++i) {
     86        if (records[i].scan == scan) {
     87            if (remove) {
     88                deleted_record = records[i];
     89                // Move rest down, and decrease count
     90                while (i + 1 < nrecs) {
     91                    records[i] = records[i + 1];
     92                    ++i;
     93                }
     94                --nrecs;
     95                result = &deleted_record;
     96            } else {
     97                result = &records[i];
     98            }
     99            break;
     100        }
     101    }
     102    return result;
     103}
     104
     105void KeyRecorder::storeKey(int scan, int code, int state, const QString& text)
     106{
     107    Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS,
     108               "Internal KeyRecorder",
     109               "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS");
     110
     111    if (nrecs == QT_MAX_KEY_RECORDINGS) {
     112        qWarning("Qt: Internal keyboard buffer overflow");
     113        return;
     114    }
     115    records[nrecs++] = KeyRecord(scan, code, state, text);
     116}
     117
     118void KeyRecorder::clearKeys()
     119{
     120    nrecs = 0;
     121}
     122// Key recorder ------------------------------------------------------[ end ] --
     123
     124// Key translation -------------------------------------------------[ start ] --
     125// Meaning of values:
     126//             0 = Character output key, needs keyboard driver mapping
     127//   Key_unknown = Unknown Virtual Key, no translation possible, ignore
     128static const uint KeyTbl[] = { // Keyboard mapping table
     129                        // Dec |  Hex | PM Virtual key
     130    Qt::Key_unknown,    //   0   0x00
     131    Qt::Key_unknown,    //   1   0x01   VK_BUTTON1          | Mouse button 1
     132    Qt::Key_unknown,    //   2   0x02   VK_BUTTON2          | Mouse button 2
     133    Qt::Key_unknown,    //   3   0x03   VK_BUTTON3          | Mouse button 3
     134    Qt::Key_Cancel,     //   4   0x04   VK_BREAK            | Control-Break processing
     135    Qt::Key_Backspace,  //   5   0x05   VK_BACKSPACE        | BackSpace key
     136    Qt::Key_Tab,        //   6   0x06   VK_TAB              | Tab key
     137    Qt::Key_Backtab,    //   7   0x07   VK_BACKTAB          | Shift+Tab key
     138    Qt::Key_Return,     //   8   0x08   VK_RETURN           | Enter key
     139    Qt::Key_Shift,      //   9   0x09   VK_SHIFT            | Shift key
     140    Qt::Key_Control,    //  10   0x0A   VK_CTRL             | Ctrl key
     141    Qt::Key_Alt,        //  11   0x0B   VK_ALT              | Alt key
     142    Qt::Key_Alt,        //  12   0x0C   VK_ALTGRAF          | AltGr key
     143    Qt::Key_Pause,      //  13   0x0D   VK_PAUSE            | Pause key
     144    Qt::Key_CapsLock,   //  14   0x0E   VK_CAPSLOCK         | Caps-Lock
     145    Qt::Key_Escape,     //  15   0x0F   VK_ESC              | Esc key
     146    Qt::Key_Space,      //  16   0x10   VK_SPACE            | Spacebar
     147    Qt::Key_PageUp,     //  17   0x11   VK_PAGEUP           | Page Up key
     148    Qt::Key_PageDown,   //  18   0x12   VK_PAGEDOWN         | Page Down key
     149    Qt::Key_End,        //  19   0x13   VK_END              | End key
     150    Qt::Key_Home,       //  20   0x14   VK_HOME             | Home key
     151    Qt::Key_Left,       //  21   0x15   VK_LEFT             | Left arrow key
     152    Qt::Key_Up,         //  22   0x16   VK_UP               | Up arrow key
     153    Qt::Key_Right,      //  23   0x17   VK_RIGHT            | Right arrow key
     154    Qt::Key_Down,       //  24   0x18   VK_DOWN             | Down arrow key
     155    Qt::Key_Print,      //  25   0x19   VK_PRINTSCRN        | Print Screen key
     156    Qt::Key_Insert,     //  26   0x1A   VK_INSERT           | Ins key
     157    Qt::Key_Delete,     //  27   0x1B   VK_DELETE           | Del key
     158    Qt::Key_NumLock,    //  28   0x1C   VK_SCROLL           | Scroll Lock key
     159    Qt::Key_ScrollLock, //  29   0x1D   VK_NUMLOCK          | Num Lock key
     160    Qt::Key_Enter,      //  30   0x1E   VK_ENTER            | Enter (Numpad) key
     161    Qt::Key_SysReq,     //  31   0x1F   VK_SYSRQ            | SysReq key
     162    Qt::Key_F1,         //  32   0x20   VK_F1               | F1 key
     163    Qt::Key_F2,         //  33   0x21   VK_F2               | F2 key
     164    Qt::Key_F3,         //  34   0x22   VK_F3               | F3 key
     165    Qt::Key_F4,         //  35   0x23   VK_F4               | F4 key
     166    Qt::Key_F5,         //  36   0x24   VK_F5               | F5 key
     167    Qt::Key_F6,         //  37   0x25   VK_F6               | F6 key
     168    Qt::Key_F7,         //  38   0x26   VK_F7               | F7 key
     169    Qt::Key_F8,         //  39   0x27   VK_F8               | F8 key
     170    Qt::Key_F9,         //  40   0x28   VK_F9               | F9 key
     171    Qt::Key_F10,        //  41   0x29   VK_F10              | F10 key
     172    Qt::Key_F11,        //  42   0x2A   VK_F11              | F11 key
     173    Qt::Key_F12,        //  43   0x2B   VK_F12              | F12 key
     174    Qt::Key_F13,        //  44   0x2C   VK_F13              | F13 key
     175    Qt::Key_F14,        //  45   0x2D   VK_F14              | F14 key
     176    Qt::Key_F15,        //  46   0x2E   VK_F15              | F15 key
     177    Qt::Key_F16,        //  47   0x2F   VK_F16              | F16 key
     178    Qt::Key_F17,        //  48   0x30   VK_F17              | F17 key
     179    Qt::Key_F18,        //  49   0x31   VK_F18              | F18 key
     180    Qt::Key_F19,        //  50   0x32   VK_F19              | F19 key
     181    Qt::Key_F20,        //  51   0x33   VK_F20              | F20 key
     182    Qt::Key_F21,        //  52   0x34   VK_F21              | F21 key
     183    Qt::Key_F22,        //  53   0x35   VK_F22              | F22 key
     184    Qt::Key_F23,        //  54   0x36   VK_F23              | F23 key
     185    Qt::Key_F24,        //  55   0x37   VK_F24              | F24 key
     186    Qt::Key_unknown,    //  56   0x38   VK_ENDDRAG          | ???
     187    Qt::Key_Clear,      //  57   0x39   VK_CLEAR            | Clear key
     188    Qt::Key_unknown,    //  58   0x3A   VK_EREOF            | ???
     189    Qt::Key_unknown,    //  59   0x3B   VK_PA1              | ???
     190    Qt::Key_unknown,    //  60   0x3C   VK_ATTN             | ???
     191    Qt::Key_unknown,    //  61   0x3D   VK_CRSEL            | ???
     192    Qt::Key_unknown,    //  62   0x3E   VK_EXSEL            | ???
     193    Qt::Key_unknown,    //  63   0x3F   VK_COPY             | ???
     194    Qt::Key_unknown,    //  64   0x40   VK_BLK1             | ???
     195    Qt::Key_unknown,    //  65   0x41   VK_BLK2             | ???
     196};
     197
     198// Key translation ---------------------------------------------------[ end ]---
     199
     200// QETWidget class is only for accessing the sendSpontaneousEvent function in QApplication
     201class QETWidget : public QWidget {
     202public:
     203    static bool sendSpontaneousEvent(QObject *r, QEvent *e)
     204    { return QApplication::sendSpontaneousEvent(r, e); }
     205};
     206
     207// Keyboard map private --------------------------------------------[ start ]---
    52208
    53209QKeyMapperPrivate::QKeyMapperPrivate()
    54210{
    55     // @todo implement
     211    // State holder for LWIN/RWIN and ALTGr keys
     212    // (ALTGr is also necessary since OS/2 doesn't report ALTGr as KC_ALT)
     213    extraKeyState = 0;
     214
     215   // @todo implement
    56216}
    57217
     
    74234}
    75235
    76 // QKeyMapper (PM) implementation -------------------------------------------------[ start ]---
     236static inline int asciiToKeycode(char a, int state)
     237{
     238    if (a >= 'a' && a <= 'z')
     239        a = toupper(a);
     240    if ((state & Qt::ControlModifier) != 0) {
     241        if (a >= 0 && a <= 31)              // Ctrl+@..Ctrl+A..CTRL+Z..Ctrl+_
     242            a += '@';                       // to @..A..Z.._
     243    }
     244    return a & 0xff;
     245}
     246
     247bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, const QMSG &qmsg, bool grab)
     248{
     249    Q_Q(QKeyMapper);
     250
     251    bool k0 = false;
     252    bool k1 = false;
     253
     254    CHRMSG chm = *CHARMSG(&qmsg.msg);
     255
     256    // we combine the flags from the message with the raw chr value and pass
     257    // them as QKeyEvent::nativeModifiers(). Together with chr.vkey passed as
     258    // nativeVirtualKey() and chr.scancode as nativeScanCode(), the Qt
     259    // application gets the full key message details (except the repeat count).
     260    quint32 nativeMods = chm.fs | chm.chr << 16;
     261
     262    // Get the modifier states (may be altered later, depending on key code)
     263    int state = 0;
     264    if (chm.fs & KC_SHIFT)
     265        state |= Qt::ShiftModifier;
     266    if (chm.fs & KC_CTRL)
     267        state |= Qt::ControlModifier;
     268    if (chm.fs & KC_ALT)
     269        state |= Qt::AltModifier;
     270
     271    // Translate VK_* (native) -> Key_* (Qt) keys
     272    int code = 0;
     273    if ((chm.fs & KC_VIRTUALKEY)) {
     274        if (chm.vkey == 0) {
     275            // The only known situation when KC_VIRTUALKEY is present but
     276            // vkey is zero is when Alt+Shift is pressed to switch the
     277            // keyboard layout state from latin to national and back.
     278            // It seems that this way the system informs applications about
     279            // layout changes: chm.chr is 0xF1 when the user switches
     280            // to the national layout (i.e. presses Alt + Left Shift)
     281            // and 0xF0 when he switches back (presses Alt + Right Shift).
     282            // We assume this and restore fs, vkey, scancode and chr accordingly.
     283            if (chm.chr == 0xF0 || chm.chr == 0xF1) {
     284                chm.fs |= KC_ALT | KC_SHIFT;
     285                chm.vkey = VK_SHIFT;
     286                chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36;
     287                chm.chr = 0;
     288                state |= Qt::AltModifier | Qt::ShiftModifier;
     289                // code will be assigned by the normal procedure below
     290            }
     291        } else if (chm.vkey == VK_ALTGRAF) {
     292            if (!(chm.fs & KC_KEYUP))
     293                extraKeyState |= Qt::AltModifier;
     294            else
     295                extraKeyState &= ~Qt::AltModifier;
     296        }
     297        if (chm.vkey < sizeof(KeyTbl))
     298            code = KeyTbl[chm.vkey];
     299    }
     300
     301    // detect some special keys that don't have a virtual key but have a pseudo
     302    // char code in the high byte of chm.chr (probably this is less
     303    // device-dependent than scancode)
     304    switch (chm.chr) {
     305        case 0xEC00: // LWIN
     306        case 0xED00: // RWIN
     307            code = Qt::Key_Meta;
     308            if (!(chm.fs & KC_KEYUP))
     309                extraKeyState |= Qt::MetaModifier;
     310            else
     311                extraKeyState &= ~Qt::MetaModifier;
     312            break;
     313        case 0xEE00: // WINAPP (menu with arrow)
     314            code = Qt::Key_Menu;
     315            break;
     316        case 0x5600: // additional '\' (0x56 is actually its scancode)
     317            chm.chr = state & Qt::ShiftModifier ? '|' : '\\';
     318            break;
     319    }
     320
     321    // update state after updating extraKeyState
     322    if (extraKeyState & Qt::AltModifier)
     323        state |= Qt::AltModifier;
     324    if (extraKeyState & Qt::MetaModifier)
     325        state |= Qt::MetaModifier;
     326
     327    // Invert state logic:
     328    // If the key actually pressed is a modifier key, then we remove its modifier key from the
     329    // state, since a modifier-key can't have itself as a modifier
     330    //
     331    // ### QKeyEvent::modifiers() already does this inversion which in result
     332    // cancels the inversion we do below and therefore makes the above statement
     333    // incorrect! It looks like the inversion block should be removed from this
     334    // function but it is left here for compatibility with other platforms which
     335    // also have this bug.
     336    //
     337    if (code == Qt::Key_Control)
     338        state = state ^ Qt::ControlModifier;
     339    else if (code == Qt::Key_Shift)
     340        state = state ^ Qt::ShiftModifier;
     341    else if (code == Qt::Key_Alt)
     342        state = state ^ Qt::AltModifier;
     343    else if (code == Qt::Key_Meta)
     344        state = state ^ Qt::MetaModifier;
     345
     346    // KEYDOWN -----------------------------------------------------------------
     347    if (!(chm.fs & KC_KEYUP)) {
     348        // Get the last record of this key press, so we can validate the current state
     349        // The record is not removed from the list
     350        KeyRecord *rec = key_recorder.findKey(chm.scancode, false);
     351
     352        // If rec's state doesn't match the current state, something has changed behind our back
     353        // (Consumed by modal widget is one possibility) So, remove the record from the list
     354        // This will stop the auto-repeat of the key, should a modifier change, for example
     355        if (rec && rec->state != state) {
     356            key_recorder.findKey(chm.scancode, true);
     357            rec = 0;
     358        }
     359
     360        // If we have a record, it means that the key is already pressed, the state is the same
     361        // so, we have an auto-repeating key
     362        if (rec) {
     363            Q_ASSERT(!code || code == rec->code);
     364            if (rec->code < Qt::Key_Shift || rec->code > Qt::Key_ScrollLock) {
     365                k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code,
     366                                     Qt::KeyboardModifier(state), rec->text, true, 0,
     367                                     chm.scancode, chm.vkey, nativeMods);
     368                k1 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, rec->code,
     369                                     Qt::KeyboardModifier(state), rec->text, true, 0,
     370                                     chm.scancode, chm.vkey, nativeMods);
     371            }
     372        }
     373        // No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
     374        // and store the key data into our records.
     375        else {
     376            QString text;
     377            if (chm.chr) {
     378                // Note: We ignore the KC_CHAR flag when generating text for the
     379                // key in order to get correct (non-null) text even for Alt+Letter
     380                // combinations (that don't have KC_CHAR set) because processing
     381                // Alt+Letter shortcuts for non-ASCII letters in widgets (e.g.
     382                // QPushButton) depends on that.
     383                if ((chm.fs & (KC_VIRTUALKEY | KC_CHAR)) == KC_CHAR && (chm.chr & 0xFF00)) {
     384                    // We assime we get a DBCS char if the above condition is met.
     385                    // DBCS chars seem to have KC_CHAR set but not KC_VIRTUALKEY; we
     386                    // use this to prevent keys like ESC (chm=0x011B) with the
     387                    // non-zero high byte to be mistakenly recognized as DBCS.
     388                    text = QString::fromLocal8Bit((char *)&chm.chr, 2);
     389                } else if (chm.chr & 0xFF) {
     390                    text = QString::fromLocal8Bit((char*)&chm.chr, 1);
     391                }
     392
     393                Q_ASSERT(code || !text.isEmpty()); // we must provide the key code
     394                if (!code && !text.isEmpty()) {
     395                    if (!text[0].row())
     396                        code = asciiToKeycode(text[0].cell(), state);
     397                    else
     398                        code = text[0].toUpper().unicode();
     399                }
     400            }
     401            key_recorder.storeKey(chm.scancode, code, state, text);
     402            k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code,
     403                                 Qt::KeyboardModifier(state), text, false, 0,
     404                                 chm.scancode, chm.vkey, nativeMods);
     405        }
     406    }
     407    // KEYUP -------------------------------------------------------------------
     408    else {
     409        // Try to locate the key in our records, and remove it if it exists.
     410        // The key may not be in our records if, for example, the down event was handled by
     411        // PM natively, or our window gets focus while a key is already press, but now gets
     412        // the key release event.
     413        KeyRecord* rec = key_recorder.findKey(chm.scancode, true);
     414        if (!rec) {
     415            // Someone ate the key down event
     416        } else {
     417            k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code,
     418                                 Qt::KeyboardModifier(state), rec->text, false, 0,
     419                                 chm.scancode, chm.vkey, nativeMods);
     420        }
     421    }
     422
     423    // Return true, if a QKeyEvent was sent to a widget
     424    return k0 || k1;
     425}
     426
     427// QKeyMapper (PM) implementation ----------------------------------[ start ]---
    77428
    78429bool QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
     
    82433                              bool *)
    83434{
    84     // @todo implement
    85     return false;
     435    Q_UNUSED(count);
     436
     437#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
     438    if (type == QEvent::KeyPress
     439        && !grab
     440        && QApplicationPrivate::instance()->use_compat()) {
     441        // send accel events if the keyboard is not grabbed
     442        QKeyEventEx a(type, code, modifiers,
     443                      text, autorepeat, qMax(1, int(text.length())),
     444                      nativeScanCode, nativeVirtualKey, nativeModifiers);
     445        if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &a))
     446            return true;
     447    }
     448#endif
     449    if (!widget->isEnabled())
     450        return false;
     451
     452    QKeyEventEx e(type, code, modifiers,
     453                  text, autorepeat, qMax(1, int(text.length())),
     454                  nativeScanCode, nativeVirtualKey, nativeModifiers);
     455    QETWidget::sendSpontaneousEvent(widget, &e);
     456
     457    return e.isAccepted();
    86458}
    87459
Note: See TracChangeset for help on using the changeset viewer.