source: branches/4.5.1/src/gui/kernel/qkeymapper_pm.cpp@ 559

Last change on this file since 559 was 492, checked in by Dmitry A. Kuminov, 15 years ago

gui: Fixed: Numpad keys with numbers reported wrong key codes when Numpad is ON (#121).

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Date Revision Author Id
File size: 34.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** Copyright (C) 2009 netlabs.org. OS/2 parts.
7**
8** This file is part of the QtGui module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial Usage
12** Licensees holding valid Qt Commercial licenses may use this file in
13** accordance with the Qt Commercial License Agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and Nokia.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Nokia gives you certain
26** additional rights. These rights are described in the Nokia Qt LGPL
27** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28** package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you are unsure which license is appropriate for your use, please
39** contact the sales department at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qkeymapper_p.h"
45#include "qapplication_p.h"
46#include "qevent_p.h"
47#include "qwidget.h"
48
49#include "qt_os2.h"
50
51//#define DEBUG_KEYMAPPER
52
53// BIDI API --------------------------------------------------------[ start ] --
54// copied from pmbidi.h from the OS/2 toolkit as Innotek GCC headers lack this
55
56#define KL_LATIN 0x00000000
57#define KL_NATIONAL 0x00000001
58
59ULONG APIENTRY WinSetKbdLayer (HWND hwnd,
60 ULONG idKbdLayer,
61 ULONG flFlags);
62
63ULONG APIENTRY WinQueryKbdLayer (HWND hwnd);
64
65ULONG APIENTRY WinQueryKbdLayout (HWND hwndDesktop);
66
67BOOL APIENTRY WinSetKbdLayout (HWND hwndDesktop,
68 ULONG idKbdLayout);
69
70// BIDI API ----------------------------------------------------------[ end ] --
71
72QT_BEGIN_NAMESPACE
73
74// Key recorder ----------------------------------------------------[ start ] --
75struct KeyRecord {
76 KeyRecord(int _scan, int _code, int _state, const QString &_text)
77 : scan(_scan), code(_code), state(_state), text(_text) {}
78 KeyRecord() {}
79
80 int scan;
81 int code;
82 int state;
83 QString text;
84};
85
86static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers...
87
88struct KeyRecorder
89{
90 KeyRecorder() : nrecs(0) {}
91
92 inline KeyRecord *findKey(int scan, bool remove);
93 inline void storeKey(int scan, int code, int state, const QString& text);
94 inline void clearKeys();
95
96 int nrecs;
97 KeyRecord deleted_record; // A copy of last entry removed from records[]
98 KeyRecord records[QT_MAX_KEY_RECORDINGS];
99};
100static KeyRecorder key_recorder;
101
102KeyRecord *KeyRecorder::findKey(int scan, bool remove)
103{
104 KeyRecord *result = 0;
105
106 if(scan == 0) // DBCS chars or user-injected keys
107 return result;
108
109 for (int i = 0; i < nrecs; ++i) {
110 if (records[i].scan == scan) {
111 if (remove) {
112 deleted_record = records[i];
113 // Move rest down, and decrease count
114 while (i + 1 < nrecs) {
115 records[i] = records[i + 1];
116 ++i;
117 }
118 --nrecs;
119 result = &deleted_record;
120 } else {
121 result = &records[i];
122 }
123 break;
124 }
125 }
126 return result;
127}
128
129void KeyRecorder::storeKey(int scan, int code, int state, const QString& text)
130{
131 if(scan == 0 && code == 0) // DBCS chars or user-injected keys
132 return;
133
134 Q_ASSERT_X(nrecs != QT_MAX_KEY_RECORDINGS,
135 "Internal KeyRecorder",
136 "Keyboard recorder buffer overflow, consider increasing QT_MAX_KEY_RECORDINGS");
137
138 if (nrecs == QT_MAX_KEY_RECORDINGS) {
139 qWarning("Qt: Internal keyboard buffer overflow");
140 return;
141 }
142 records[nrecs++] = KeyRecord(scan, code, state, text);
143}
144
145void KeyRecorder::clearKeys()
146{
147 nrecs = 0;
148}
149// Key recorder ------------------------------------------------------[ end ] --
150
151// Key translation -------------------------------------------------[ start ] --
152// Meaning of values:
153// 0 = Character output key, needs keyboard driver mapping
154// Key_unknown = Unknown Virtual Key, no translation possible, ignore
155static const uint KeyTbl[] = { // Keyboard mapping table
156 // Dec | Hex | PM Virtual key
157 Qt::Key_unknown, // 0 0x00
158 Qt::Key_unknown, // 1 0x01 VK_BUTTON1 | Mouse button 1
159 Qt::Key_unknown, // 2 0x02 VK_BUTTON2 | Mouse button 2
160 Qt::Key_unknown, // 3 0x03 VK_BUTTON3 | Mouse button 3
161 Qt::Key_Cancel, // 4 0x04 VK_BREAK | Control-Break processing
162 Qt::Key_Backspace, // 5 0x05 VK_BACKSPACE | BackSpace key
163 Qt::Key_Tab, // 6 0x06 VK_TAB | Tab key
164 Qt::Key_Backtab, // 7 0x07 VK_BACKTAB | Shift+Tab key
165 Qt::Key_Return, // 8 0x08 VK_RETURN | Enter key
166 Qt::Key_Shift, // 9 0x09 VK_SHIFT | Shift key
167 Qt::Key_Control, // 10 0x0A VK_CTRL | Ctrl key
168 Qt::Key_Alt, // 11 0x0B VK_ALT | Alt key
169 Qt::Key_Alt, // 12 0x0C VK_ALTGRAF | AltGr key
170 Qt::Key_Pause, // 13 0x0D VK_PAUSE | Pause key
171 Qt::Key_CapsLock, // 14 0x0E VK_CAPSLOCK | Caps-Lock
172 Qt::Key_Escape, // 15 0x0F VK_ESC | Esc key
173 Qt::Key_Space, // 16 0x10 VK_SPACE | Spacebar
174 Qt::Key_PageUp, // 17 0x11 VK_PAGEUP | Page Up key
175 Qt::Key_PageDown, // 18 0x12 VK_PAGEDOWN | Page Down key
176 Qt::Key_End, // 19 0x13 VK_END | End key
177 Qt::Key_Home, // 20 0x14 VK_HOME | Home key
178 Qt::Key_Left, // 21 0x15 VK_LEFT | Left arrow key
179 Qt::Key_Up, // 22 0x16 VK_UP | Up arrow key
180 Qt::Key_Right, // 23 0x17 VK_RIGHT | Right arrow key
181 Qt::Key_Down, // 24 0x18 VK_DOWN | Down arrow key
182 Qt::Key_Print, // 25 0x19 VK_PRINTSCRN | Print Screen key
183 Qt::Key_Insert, // 26 0x1A VK_INSERT | Ins key
184 Qt::Key_Delete, // 27 0x1B VK_DELETE | Del key
185 Qt::Key_ScrollLock, // 28 0x1C VK_SCROLL | Scroll Lock key
186 Qt::Key_NumLock, // 29 0x1D VK_NUMLOCK | Num Lock key
187 Qt::Key_Enter, // 30 0x1E VK_ENTER | Enter (Numpad) key
188 Qt::Key_SysReq, // 31 0x1F VK_SYSRQ | SysReq key
189 Qt::Key_F1, // 32 0x20 VK_F1 | F1 key
190 Qt::Key_F2, // 33 0x21 VK_F2 | F2 key
191 Qt::Key_F3, // 34 0x22 VK_F3 | F3 key
192 Qt::Key_F4, // 35 0x23 VK_F4 | F4 key
193 Qt::Key_F5, // 36 0x24 VK_F5 | F5 key
194 Qt::Key_F6, // 37 0x25 VK_F6 | F6 key
195 Qt::Key_F7, // 38 0x26 VK_F7 | F7 key
196 Qt::Key_F8, // 39 0x27 VK_F8 | F8 key
197 Qt::Key_F9, // 40 0x28 VK_F9 | F9 key
198 Qt::Key_F10, // 41 0x29 VK_F10 | F10 key
199 Qt::Key_F11, // 42 0x2A VK_F11 | F11 key
200 Qt::Key_F12, // 43 0x2B VK_F12 | F12 key
201 Qt::Key_F13, // 44 0x2C VK_F13 | F13 key
202 Qt::Key_F14, // 45 0x2D VK_F14 | F14 key
203 Qt::Key_F15, // 46 0x2E VK_F15 | F15 key
204 Qt::Key_F16, // 47 0x2F VK_F16 | F16 key
205 Qt::Key_F17, // 48 0x30 VK_F17 | F17 key
206 Qt::Key_F18, // 49 0x31 VK_F18 | F18 key
207 Qt::Key_F19, // 50 0x32 VK_F19 | F19 key
208 Qt::Key_F20, // 51 0x33 VK_F20 | F20 key
209 Qt::Key_F21, // 52 0x34 VK_F21 | F21 key
210 Qt::Key_F22, // 53 0x35 VK_F22 | F22 key
211 Qt::Key_F23, // 54 0x36 VK_F23 | F23 key
212 Qt::Key_F24, // 55 0x37 VK_F24 | F24 key
213 Qt::Key_unknown, // 56 0x38 VK_ENDDRAG | ???
214 Qt::Key_Clear, // 57 0x39 VK_CLEAR | Clear key
215 Qt::Key_unknown, // 58 0x3A VK_EREOF | ???
216 Qt::Key_unknown, // 59 0x3B VK_PA1 | ???
217 Qt::Key_unknown, // 60 0x3C VK_ATTN | ???
218 Qt::Key_unknown, // 61 0x3D VK_CRSEL | ???
219 Qt::Key_unknown, // 62 0x3E VK_EXSEL | ???
220 Qt::Key_unknown, // 63 0x3F VK_COPY | ???
221 Qt::Key_unknown, // 64 0x40 VK_BLK1 | ???
222 Qt::Key_unknown, // 65 0x41 VK_BLK2 | ???
223};
224
225// Converts known accent symbols to their Key_Dead_ equivalents.
226static inline int deadCharToDeadKeyCode(const QChar &ch)
227{
228 switch (ch.unicode()) {
229 case 0x0060: return Qt::Key_Dead_Grave;
230 case 0x00B4: return Qt::Key_Dead_Acute;
231 case 0x005E: return Qt::Key_Dead_Circumflex;
232 case 0x007E: return Qt::Key_Dead_Tilde;
233 case 0x00AF: return Qt::Key_Dead_Macron;
234 case 0x02D8: return Qt::Key_Dead_Breve;
235 case 0x02D9: return Qt::Key_Dead_Abovedot;
236 case 0x00A8: return Qt::Key_Dead_Diaeresis;
237 case 0x02DA: return Qt::Key_Dead_Abovering;
238 case 0x02DD: return Qt::Key_Dead_Doubleacute;
239 case 0x02C7: return Qt::Key_Dead_Caron;
240 case 0x00B8: return Qt::Key_Dead_Cedilla;
241 case 0x02DB: return Qt::Key_Dead_Ogonek;
242 case 0x1DA5: return Qt::Key_Dead_Iota; // @todo is that correct???
243 case 0x3099: return Qt::Key_Dead_Voiced_Sound;
244 case 0x309A: return Qt::Key_Dead_Semivoiced_Sound;
245 case 0x0323: return Qt::Key_Dead_Belowdot;
246 case 0x02DE: return Qt::Key_Dead_Hook;
247 case 0x031B: return Qt::Key_Dead_Horn;
248 default: break;
249 }
250
251 // otherwise, return unmodified uppercase unicode value (similar to other
252 // cases)
253 return ch.unicode();
254}
255
256// Key translation ---------------------------------------------------[ end ]---
257
258// QETWidget class is only for accessing the sendSpontaneousEvent function in QApplication
259class QETWidget : public QWidget {
260public:
261 static bool sendSpontaneousEvent(QObject *r, QEvent *e)
262 { return QApplication::sendSpontaneousEvent(r, e); }
263};
264
265// Keyboard map private --------------------------------------------[ start ]---
266
267/*
268 \internal
269
270 An OS/2 KeyboardLayoutItem has 8 possible states meaningful for Qt:
271 1. Unmodified
272 2. Shift
273 3. Control
274 4. Control + Shift
275 5. Alt
276 6. Alt + Shift
277 7. Alt + Control
278 8. Alt + Control + Shift
279*/
280struct KeyboardLayoutItem {
281 int qtKey[8][2]; // Can be any Qt::Key_<foo>, or unicode character.
282 // The second index is for different keyboard layouts
283 ULONG layoutIds[2]; // Latin layout ID + National layout ID
284 enum { QtKeySize = sizeof(qtKey) / sizeof(qtKey[0]) };
285};
286
287// Possible Qt modifier states. Must match KbdModsTbl
288static const Qt::KeyboardModifiers QtModsTbl[KeyboardLayoutItem::QtKeySize] = {
289 Qt::NoModifier, // 0
290 Qt::ShiftModifier, // 1
291 Qt::ControlModifier, // 2
292 Qt::ControlModifier | Qt::ShiftModifier, // 3
293 Qt::AltModifier, // 4
294 Qt::AltModifier | Qt::ShiftModifier, // 5
295 Qt::AltModifier | Qt::ControlModifier, // 6
296 Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
297};
298
299// Possible OS/2 keyboard modifier states. Must match QtModsTbl
300static const USHORT KbdModsTbl[] = {
301 0, // 0
302 KBDSTF_RIGHTSHIFT, // 1
303 KBDSTF_CONTROL, // 2
304 KBDSTF_CONTROL | KBDSTF_RIGHTSHIFT, // 3
305 KBDSTF_ALT, // 4
306 KBDSTF_ALT | KBDSTF_RIGHTSHIFT, // 5
307 KBDSTF_ALT | KBDSTF_CONTROL, // 6
308 KBDSTF_ALT | KBDSTF_RIGHTSHIFT | KBDSTF_CONTROL, // 7
309};
310
311QKeyMapperPrivate::QKeyMapperPrivate()
312{
313 // State holder for LWIN/RWIN and ALTGr keys
314 // (ALTGr is also necessary since OS/2 doesn't report ALTGr as KC_ALT)
315 extraKeyState = 0;
316
317 memset(keyLayout, 0, sizeof(keyLayout));
318}
319
320QKeyMapperPrivate::~QKeyMapperPrivate()
321{
322 clearMappings();
323}
324
325void QKeyMapperPrivate::clearMappings()
326{
327 for (int i = 0; i < KeyLayoutSize; ++i) {
328 if (keyLayout[i]) {
329 delete keyLayout[i];
330 keyLayout[i] = 0;
331 }
332 }
333}
334
335QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
336{
337 QList<int> result;
338
339 KeyboardLayoutItem *kbItem = keyLayout[e->nativeScanCode()];
340 if(!kbItem) {
341#ifdef DEBUG_KEYMAPPER
342 qDebug("QKeyMapperPrivate::possibleKeys: none");
343#endif
344 return result;
345 }
346
347 int baseKey0 = kbItem->qtKey[0][0] ? kbItem->qtKey[0][0] :
348 e->key() && e->key() != Qt::Key_unknown ? e->key() :
349 e->text().at(0).unicode();
350
351 int baseKey1 = baseKey0;
352 if (kbItem->layoutIds[1])
353 kbItem->qtKey[0][1] ? kbItem->qtKey[0][1] :
354 e->key() && e->key() != Qt::Key_unknown ? e->key() :
355 e->text().at(0).unicode();
356
357 Qt::KeyboardModifiers keyMods = e->modifiers();
358
359 // The base key is _always_ valid, of course
360 result << int(baseKey0 + keyMods);
361 if (baseKey1 != baseKey0)
362 result << int(baseKey1 + keyMods);
363
364 // go through both keyboard layouts
365 for (int j = 0; j < 2; ++j) {
366 // check if we skipped the layout in updateKeyMap() and skip too if so
367 if (!kbItem->layoutIds[j])
368 continue;
369 // go through all modifiers
370 for(int i = 0; i < KeyboardLayoutItem::QtKeySize; ++i) {
371 Qt::KeyboardModifiers neededMods = QtModsTbl[i];
372 int key = kbItem->qtKey[i][j];
373 if (key && key != baseKey0 && key != baseKey1 &&
374 ((keyMods & neededMods) == neededMods)) {
375 int k = int(key + (keyMods & ~neededMods));
376 if (!result.contains(k))
377 result << k;
378 }
379 }
380 }
381
382#ifdef DEBUG_KEYMAPPER
383 qDebug("QKeyMapperPrivate::possibleKeys:");
384 foreach(int k, result)
385 qDebug(" 0x%x", k);
386#endif
387
388 return result;
389}
390
391void QKeyMapperPrivate::updateKeyMap(const QMSG &qmsg)
392{
393 CHRMSG chm = *CHARMSG(&qmsg.msg);
394
395 // it may be a keyboard layout change message, see translateKeyEvent()
396 // for details
397 if ((chm.fs & KC_VIRTUALKEY) && chm.vkey == 0) {
398 if (chm.chr == 0xF0 || chm.chr == 0xF1) {
399 chm.fs |= KC_ALT | KC_SHIFT;
400 chm.vkey = VK_SHIFT;
401 chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36;
402 chm.chr = 0;
403 }
404 }
405
406 if (!chm.scancode)
407 return;
408
409 if (!keyLayout[chm.scancode])
410 keyLayout[chm.scancode] = new KeyboardLayoutItem;
411
412 KBDTRANS kt;
413 ULONG curLayerId = WinQueryKbdLayer(qmsg.hwnd);
414
415#ifdef DEBUG_KEYMAPPER
416 int layoutsChanged = 0;
417#endif
418
419 // go through both keyboard layouts
420 for (int j = 0; j < 2; ++j) {
421 WinSetKbdLayer(qmsg.hwnd, j ? KL_NATIONAL : KL_LATIN, 0);
422 // check if the data is still valid and skip if so. Also skip the
423 // National layout if it's the same as Latin
424 ULONG layoutId = WinQueryKbdLayout(HWND_DESKTOP);
425 if (keyLayout[chm.scancode]->layoutIds[j] == layoutId ||
426 (j && keyLayout[chm.scancode]->layoutIds[0] == layoutId))
427 continue;
428 keyLayout[chm.scancode]->layoutIds[j] = layoutId;
429
430 // now go through all modifiers
431 for (int i = 0; i < KeyboardLayoutItem::QtKeySize; ++i) {
432 // reset all kbd states and modifiers
433 memset(&kt, 0, sizeof(KBDTRANS));
434 kt.chScan = chm.scancode;
435 kt.fsState = KbdModsTbl[i];
436 APIRET arc = KbdXlate(&kt, 0);
437 Q_ASSERT(arc == NO_ERROR);
438 keyLayout[chm.scancode]->qtKey[i][j] =
439 QString::fromLocal8Bit((char*)&kt.chChar, 1)[0].toUpper().unicode();
440 }
441#ifdef DEBUG_KEYMAPPER
442 ++layoutsChanged;
443#endif
444 }
445
446 // restore the layout
447 WinSetKbdLayer(qmsg.hwnd, curLayerId, 0);
448
449#ifdef DEBUG_KEYMAPPER
450 if (layoutsChanged) {
451 qDebug("QKeyMapperPrivate::updateKeyMap: scancode 0x%02x", chm.scancode);
452 for (int i = 0; i < KeyboardLayoutItem::QtKeySize; ++i) {
453 qDebug(" [%d] (0x%04x, '%lc') (0x%04x, '%lc')", i,
454 keyLayout[chm.scancode]->qtKey[i][0],
455 keyLayout[chm.scancode]->qtKey[i][0] < 0x20 ? ' ' :
456 keyLayout[chm.scancode]->qtKey[i][0],
457 keyLayout[chm.scancode]->qtKey[i][1],
458 keyLayout[chm.scancode]->qtKey[i][1] < 0x20 ? ' ' :
459 keyLayout[chm.scancode]->qtKey[i][1]);
460 }
461 }
462#endif
463}
464
465bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, const QMSG &qmsg, bool grab)
466{
467 Q_Q(QKeyMapper);
468
469 bool k0 = false;
470 bool k1 = false;
471
472 CHRMSG chm = *CHARMSG(&qmsg.msg);
473
474 // we combine the flags from the message with the raw chr value and pass
475 // them as QKeyEvent::nativeModifiers(). Together with chr.vkey passed as
476 // nativeVirtualKey() and chr.scancode as nativeScanCode(), the Qt
477 // application gets the full key message details (except the repeat count).
478 quint32 nativeMods = chm.fs | chm.chr << 16;
479
480 // Get the modifier states (may be altered later, depending on key code)
481 int state = 0;
482 if (chm.fs & KC_SHIFT)
483 state |= Qt::ShiftModifier;
484 if (chm.fs & KC_CTRL)
485 state |= Qt::ControlModifier;
486 if (chm.fs & KC_ALT)
487 state |= Qt::AltModifier;
488
489 // Translate VK_* (native) -> Key_* (Qt) keys
490 int code = 0;
491 if ((chm.fs & KC_VIRTUALKEY)) {
492 if (chm.vkey == 0) {
493 // The only known situation when KC_VIRTUALKEY is present but
494 // vkey is zero is when Alt+Shift is pressed to switch the
495 // keyboard layout state from latin to national and back.
496 // It seems that this way the system informs applications about
497 // layout changes: chm.chr is 0xF1 when the user switches
498 // to the national layout (i.e. presses Alt + Left Shift)
499 // and 0xF0 when he switches back (presses Alt + Right Shift).
500 // We assume this and restore fs, vkey, scancode and chr accordingly.
501 if (chm.chr == 0xF0 || chm.chr == 0xF1) {
502 chm.fs |= KC_ALT | KC_SHIFT;
503 chm.vkey = VK_SHIFT;
504 chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36;
505 chm.chr = 0;
506 state |= Qt::AltModifier | Qt::ShiftModifier;
507 // code will be assigned by the normal procedure below
508 }
509 } else if (chm.vkey == VK_ALTGRAF) {
510 if (!(chm.fs & KC_KEYUP))
511 extraKeyState |= Qt::AltModifier;
512 else
513 extraKeyState &= ~Qt::AltModifier;
514 }
515 if (chm.vkey < sizeof(KeyTbl))
516 code = KeyTbl[chm.vkey];
517 }
518
519 // detect some special keys that don't have a virtual key but have a pseudo
520 // char code in the high byte of chm.chr (probably this is less
521 // device-dependent than scancode)
522 if (!code){
523 switch (chm.chr) {
524 case 0xEC00: // LWIN
525 case 0xED00: // RWIN
526 code = Qt::Key_Meta;
527 if (!(chm.fs & KC_KEYUP))
528 extraKeyState |= Qt::MetaModifier;
529 else
530 extraKeyState &= ~Qt::MetaModifier;
531 break;
532 case 0xEE00: // WINAPP (menu with arrow)
533 code = Qt::Key_Menu;
534 break;
535 case 0x5600: // additional '\' (0x56 is actually its scancode)
536 chm.chr = state & Qt::ShiftModifier ? '|' : '\\';
537 break;
538 case 0xFA00: // Back
539 code = Qt::Key_Back;
540 break;
541 case 0xF900: // Forward
542 code = Qt::Key_Forward;
543 break;
544 case 0x2064: // Volume Mute
545 code = Qt::Key_VolumeMute;
546 break;
547 case 0x2E63: // Volume Down
548 code = Qt::Key_VolumeDown;
549 break;
550 case 0x3062: // Volume Up
551 code = Qt::Key_VolumeUp;
552 break;
553 case 0x2267: // Play/Pause
554 code = Qt::Key_MediaPlay;
555 break;
556 case 0x326D: // Web/Home
557 code = Qt::Key_HomePage;
558 break;
559 case 0xF500: // Search
560 code = Qt::Key_Search;
561 break;
562 case 0xF600: // Favorites
563 code = Qt::Key_Favorites;
564 break;
565 }
566 }
567
568 // update state after updating extraKeyState
569 if (extraKeyState & Qt::AltModifier)
570 state |= Qt::AltModifier;
571 if (extraKeyState & Qt::MetaModifier)
572 state |= Qt::MetaModifier;
573
574 // Invert state logic:
575 // If the key actually pressed is a modifier key, then we remove its modifier key from the
576 // state, since a modifier-key can't have itself as a modifier
577 //
578 // ### QKeyEvent::modifiers() already does this inversion which in result
579 // cancels the inversion we do below and therefore makes the above statement
580 // incorrect! It looks like the inversion block should be removed from this
581 // function but it is left here for compatibility with other platforms which
582 // also have this bug.
583 //
584 if (code == Qt::Key_Control)
585 state = state ^ Qt::ControlModifier;
586 else if (code == Qt::Key_Shift)
587 state = state ^ Qt::ShiftModifier;
588 else if (code == Qt::Key_Alt)
589 state = state ^ Qt::AltModifier;
590 else if (code == Qt::Key_Meta)
591 state = state ^ Qt::MetaModifier;
592
593 // detect numeric keypad keys
594 if (chm.vkey == VK_ENTER || chm.vkey == VK_NUMLOCK) {
595 // these always come from the numpad
596 state |= Qt::KeypadModifier;
597 } else if (((chm.vkey >= VK_PAGEUP && chm.vkey <= VK_DOWN) ||
598 chm.vkey == VK_INSERT || chm.vkey == VK_DELETE)) {
599 if ((chm.chr & 0xFF) != 0xE0) {
600 state |= Qt::KeypadModifier;
601 if ((state & Qt::AltModifier) && chm.vkey != VK_DELETE) {
602 // hide the key from Qt widgets and let the standard OS/2 Alt+ddd
603 // shortcut (that composed chars from typed in ASCII codes) work
604 // correctly. If we don't do that, widgets will see both individual
605 // digits (or cursor movements if NumLock is off) and the char
606 // composed by Alt+ddd.
607 return false;
608 }
609 }
610 }
611 // detect other numpad keys. OS/2 doesn't assign virtual keys to them
612 // so use scancodes (it can be device-dependent, is there a better way?)
613 switch (chm.scancode) {
614 case 0x4C: // 5
615 state |= Qt::KeypadModifier;
616 if (state & Qt::AltModifier) {
617 // hide the key from Qt (see above)
618 return false;
619 } else {
620 // scancode is zero if Numlock is set
621 if (!code)
622 code = Qt::Key_Clear;
623 }
624 break;
625 case 0x37: // *
626 // OS/2 assigns VK_PRINTSCRN to it when pressed with Shift, also
627 // it sets chr to zero when it is released with Alt or Ctrl
628 // leaving vkey as zero too, and does few other strange things --
629 // override them all
630 code = Qt::Key_Asterisk;
631 state |= Qt::KeypadModifier;
632 break;
633 case 0x5C: // /
634 code = Qt::Key_Slash;
635 // fall through
636 case 0x4A: // -
637 case 0x4E: // +
638 // the code for the above two is obtained by KbdXlate above
639 state |= Qt::KeypadModifier;
640 break;
641 }
642
643 if ((state & Qt::KeypadModifier) && (chm.fs & KC_CHAR) &&
644 chm.vkey != VK_ENTER) {
645 // if it's a numpad key and it also provides a character, it means
646 // Numlock is on. According to other platforms, key code must be the
647 // character value in this case. Reset code to 0 to have it done so.
648 // Enter is an exception as it should always produce a virtual code.
649 if (chm.fs & KC_CHAR)
650 code = 0;
651 }
652
653 // Note: code and/or chm.scancode may be zero here. We cannot ignore such
654 // events because, for example, all non-ASCII letters have zero virtual
655 // codes, and DBCS characters entered via IME have both zero virtual codes
656 // and zero scancodes. However, if both code and chm.scancode are zero
657 // (as for DBCS), storeKey()/findKey() will do nothing which means that:
658 //
659 // 1) QKeyEvents will not have the auto-repeat flag set when a key is
660 // being auto-repeated by the system;
661 // 2) there will be no QEvent::KeyRelease event corresponding to the
662 // QEvent::KeyPress event.
663 //
664 // This seems to be acceptable.
665
666 // KEYDOWN -----------------------------------------------------------------
667 if (!(chm.fs & KC_KEYUP)) {
668 // Get the last record of this key press, so we can validate the current state
669 // The record is not removed from the list
670 KeyRecord *rec = key_recorder.findKey(chm.scancode, false);
671
672 if (state == Qt::AltModifier) {
673 // Special handling of global PM hotkeys
674 switch (code) {
675 case Qt::Key_Space: {
676 // show system menu
677 HWND fId = widget->window()->internalFrameWinId();
678 HWND sysMenu = WinWindowFromID(fId, FID_SYSMENU);
679 if (!sysMenu)
680 break; // no menu for this window
681 WinPostMsg(sysMenu, MM_STARTMENUMODE,
682 MPFROM2SHORT(TRUE, FALSE), 0);
683 return true;
684 }
685 case Qt::Key_F4: {
686 // we handle this key combination ourselves because not
687 // all top-level widgets have the system menu
688 WinPostMsg(widget->window()->internalFrameWinId(),
689 WM_CLOSE, 0, 0);
690 return true;
691 }
692 default:
693 break;
694 }
695 }
696
697 // If rec's state doesn't match the current state, something has changed behind our back
698 // (Consumed by modal widget is one possibility) So, remove the record from the list
699 // This will stop the auto-repeat of the key, should a modifier change, for example
700 if (rec && rec->state != state) {
701 key_recorder.findKey(chm.scancode, true);
702 rec = 0;
703 }
704
705 // If we have a record, it means that the key is already pressed, the state is the same
706 // so, we have an auto-repeating key
707 if (rec) {
708 Q_ASSERT(!code || code == rec->code);
709 if (rec->code < Qt::Key_Shift || rec->code > Qt::Key_ScrollLock) {
710 k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code,
711 Qt::KeyboardModifier(state), rec->text, true, 0,
712 chm.scancode, chm.vkey, nativeMods);
713 k1 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, rec->code,
714 Qt::KeyboardModifier(state), rec->text, true, 0,
715 chm.scancode, chm.vkey, nativeMods);
716 }
717 }
718 // No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
719 // and store the key data into our records.
720 else {
721 QString text;
722 // we assume that we have a correct 8-bit character not only when
723 // KC_CHAR is set but also when there are one or more modifiers
724 // (in which case KC_CHAR is not present) but no KC_VIRTUALKEY.
725 // The latter is in particular important for Alt+Letter combos
726 // processing Alt+Letter shortcuts for non-ASCII letters in widgets
727 // (e.g. QPushButton) depends on that.
728 if (((chm.fs & KC_CHAR) ||
729 (!(chm.fs & KC_VIRTUALKEY) && (chm.fs & (KC_CTRL | KC_ALT | KC_SHIFT)))) &&
730 chm.chr) {
731 if (chm.chr & 0xFF00) {
732 // We assume we get a DBCS char if the above condition is met.
733 text = QString::fromLocal8Bit((char *)&chm.chr, 2);
734 } else {
735 text = QString::fromLocal8Bit((char*)&chm.chr, 1);
736 }
737
738 if (chm.fs & KC_DEADKEY) {
739 // convert dead keys to Key_Dead_* codes and set text to
740 // null to avoid interpreting them as normal chars
741 Q_ASSERT(text.size() == 1);
742 code = deadCharToDeadKeyCode(text[0]);
743 text = QString();
744 } else if (chm.fs & KC_INVALIDCOMP) {
745 // if the pressed letter is invalid for the given dead
746 // key, we set text to null as well (to meet the PM
747 // behavior) and set the code to the uppercase unicode
748 // value (similar to other cases) to have it recognized
749 Q_ASSERT(text.size() == 1);
750 code = text[0].toUpper().unicode();
751 text = QString();
752 }
753
754 Q_ASSERT(code || !text.isEmpty()); // we must provide the key code
755 if (!code && !text.isEmpty()) {
756 code = text[0].toUpper().unicode();
757 }
758
759 if ((state & Qt::ControlModifier) &&
760 !(state & Qt::KeypadModifier) && !(state & Qt::AltModifier) &&
761 !text.isEmpty() && !text[0].row()) {
762 // Ctrl + A..Z etc. produce ascii from 0x01 to 0x1F
763 int ascii = text[0].toUpper().cell();
764 if (ascii >= 0x41 && ascii <= 0x5F)
765 ascii -= 0x40;
766 // the below emulates OS/2 functionality. It differs from
767 // e.g. Win32.
768 else if (ascii == 0x36 && !(state & Qt::KeypadModifier))
769 ascii = 0x1E;
770 else if (ascii == 0x2D)
771 ascii = 0x1F;
772 else if (ascii >= 0x7B && ascii <= 0x7D)
773 ascii -= 0x60;
774 text = QChar(ascii);
775 }
776 }
777 key_recorder.storeKey(chm.scancode, code, state, text);
778 k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code,
779 Qt::KeyboardModifier(state), text, false, 0,
780 chm.scancode, chm.vkey, nativeMods);
781 }
782 }
783 // KEYUP -------------------------------------------------------------------
784 else {
785 // Try to locate the key in our records, and remove it if it exists.
786 // The key may not be in our records if, for example, the down event was handled by
787 // PM natively, or our window gets focus while a key is already press, but now gets
788 // the key release event.
789 KeyRecord* rec = key_recorder.findKey(chm.scancode, true);
790 if (!rec) {
791 // Someone ate the key down event
792 } else {
793 k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code,
794 Qt::KeyboardModifier(state), rec->text, false, 0,
795 chm.scancode, chm.vkey, nativeMods);
796 }
797 }
798
799 // Return true, if a QKeyEvent was sent to a widget
800 return k0 || k1;
801}
802
803// QKeyMapper (PM) implementation ----------------------------------[ start ]---
804
805bool QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
806 QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
807 const QString &text, bool autorepeat, int count,
808 quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
809 bool *)
810{
811 Q_UNUSED(count);
812
813#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
814 if (type == QEvent::KeyPress
815 && !grab
816 && QApplicationPrivate::instance()->use_compat()) {
817 // send accel events if the keyboard is not grabbed
818 QKeyEventEx a(type, code, modifiers,
819 text, autorepeat, qMax(1, int(text.length())),
820 nativeScanCode, nativeVirtualKey, nativeModifiers);
821 if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &a))
822 return true;
823 }
824#endif
825 if (!widget->isEnabled())
826 return false;
827
828 QKeyEventEx e(type, code, modifiers,
829 text, autorepeat, qMax(1, int(text.length())),
830 nativeScanCode, nativeVirtualKey, nativeModifiers);
831 QETWidget::sendSpontaneousEvent(widget, &e);
832
833 return e.isAccepted();
834}
835
836QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.