source: trunk/src/gui/kernel/qkeymapper_pm.cpp@ 668

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

Fixed some release warnings.

File size: 34.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2010 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
39** Nokia 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 Q_UNUSED(arc);
439 keyLayout[chm.scancode]->qtKey[i][j] =
440 QString::fromLocal8Bit((char*)&kt.chChar, 1)[0].toUpper().unicode();
441 }
442#ifdef DEBUG_KEYMAPPER
443 ++layoutsChanged;
444#endif
445 }
446
447 // restore the layout
448 WinSetKbdLayer(qmsg.hwnd, curLayerId, 0);
449
450#ifdef DEBUG_KEYMAPPER
451 if (layoutsChanged) {
452 qDebug("QKeyMapperPrivate::updateKeyMap: scancode 0x%02x", chm.scancode);
453 for (int i = 0; i < KeyboardLayoutItem::QtKeySize; ++i) {
454 qDebug(" [%d] (0x%04x, '%lc') (0x%04x, '%lc')", i,
455 keyLayout[chm.scancode]->qtKey[i][0],
456 keyLayout[chm.scancode]->qtKey[i][0] < 0x20 ? ' ' :
457 keyLayout[chm.scancode]->qtKey[i][0],
458 keyLayout[chm.scancode]->qtKey[i][1],
459 keyLayout[chm.scancode]->qtKey[i][1] < 0x20 ? ' ' :
460 keyLayout[chm.scancode]->qtKey[i][1]);
461 }
462 }
463#endif
464}
465
466bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, const QMSG &qmsg, bool grab)
467{
468 Q_Q(QKeyMapper);
469
470 bool k0 = false;
471 bool k1 = false;
472
473 CHRMSG chm = *CHARMSG(&qmsg.msg);
474
475 // we combine the flags from the message with the raw chr value and pass
476 // them as QKeyEvent::nativeModifiers(). Together with chr.vkey passed as
477 // nativeVirtualKey() and chr.scancode as nativeScanCode(), the Qt
478 // application gets the full key message details (except the repeat count).
479 quint32 nativeMods = chm.fs | chm.chr << 16;
480
481 // Get the modifier states (may be altered later, depending on key code)
482 int state = 0;
483 if (chm.fs & KC_SHIFT)
484 state |= Qt::ShiftModifier;
485 if (chm.fs & KC_CTRL)
486 state |= Qt::ControlModifier;
487 if (chm.fs & KC_ALT)
488 state |= Qt::AltModifier;
489
490 // Translate VK_* (native) -> Key_* (Qt) keys
491 int code = 0;
492 if ((chm.fs & KC_VIRTUALKEY)) {
493 if (chm.vkey == 0) {
494 // The only known situation when KC_VIRTUALKEY is present but
495 // vkey is zero is when Alt+Shift is pressed to switch the
496 // keyboard layout state from latin to national and back.
497 // It seems that this way the system informs applications about
498 // layout changes: chm.chr is 0xF1 when the user switches
499 // to the national layout (i.e. presses Alt + Left Shift)
500 // and 0xF0 when he switches back (presses Alt + Right Shift).
501 // We assume this and restore fs, vkey, scancode and chr accordingly.
502 if (chm.chr == 0xF0 || chm.chr == 0xF1) {
503 chm.fs |= KC_ALT | KC_SHIFT;
504 chm.vkey = VK_SHIFT;
505 chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36;
506 chm.chr = 0;
507 state |= Qt::AltModifier | Qt::ShiftModifier;
508 // code will be assigned by the normal procedure below
509 }
510 } else if (chm.vkey == VK_ALTGRAF) {
511 if (!(chm.fs & KC_KEYUP))
512 extraKeyState |= Qt::AltModifier;
513 else
514 extraKeyState &= ~Qt::AltModifier;
515 }
516 if (chm.vkey < sizeof(KeyTbl))
517 code = KeyTbl[chm.vkey];
518 }
519
520 // detect some special keys that don't have a virtual key but have a pseudo
521 // char code in the high byte of chm.chr (probably this is less
522 // device-dependent than scancode)
523 if (!code){
524 switch (chm.chr) {
525 case 0xEC00: // LWIN
526 case 0xED00: // RWIN
527 code = Qt::Key_Meta;
528 if (!(chm.fs & KC_KEYUP))
529 extraKeyState |= Qt::MetaModifier;
530 else
531 extraKeyState &= ~Qt::MetaModifier;
532 break;
533 case 0xEE00: // WINAPP (menu with arrow)
534 code = Qt::Key_Menu;
535 break;
536 case 0x5600: // additional '\' (0x56 is actually its scancode)
537 chm.chr = state & Qt::ShiftModifier ? '|' : '\\';
538 break;
539 case 0xFA00: // Back
540 code = Qt::Key_Back;
541 break;
542 case 0xF900: // Forward
543 code = Qt::Key_Forward;
544 break;
545 case 0x2064: // Volume Mute
546 code = Qt::Key_VolumeMute;
547 break;
548 case 0x2E63: // Volume Down
549 code = Qt::Key_VolumeDown;
550 break;
551 case 0x3062: // Volume Up
552 code = Qt::Key_VolumeUp;
553 break;
554 case 0x2267: // Play/Pause
555 code = Qt::Key_MediaPlay;
556 break;
557 case 0x326D: // Web/Home
558 code = Qt::Key_HomePage;
559 break;
560 case 0xF500: // Search
561 code = Qt::Key_Search;
562 break;
563 case 0xF600: // Favorites
564 code = Qt::Key_Favorites;
565 break;
566 }
567 }
568
569 // update state after updating extraKeyState
570 if (extraKeyState & Qt::AltModifier)
571 state |= Qt::AltModifier;
572 if (extraKeyState & Qt::MetaModifier)
573 state |= Qt::MetaModifier;
574
575 // Invert state logic:
576 // If the key actually pressed is a modifier key, then we remove its modifier key from the
577 // state, since a modifier-key can't have itself as a modifier
578 //
579 // ### QKeyEvent::modifiers() already does this inversion which in result
580 // cancels the inversion we do below and therefore makes the above statement
581 // incorrect! It looks like the inversion block should be removed from this
582 // function but it is left here for compatibility with other platforms which
583 // also have this bug.
584 //
585 if (code == Qt::Key_Control)
586 state = state ^ Qt::ControlModifier;
587 else if (code == Qt::Key_Shift)
588 state = state ^ Qt::ShiftModifier;
589 else if (code == Qt::Key_Alt)
590 state = state ^ Qt::AltModifier;
591 else if (code == Qt::Key_Meta)
592 state = state ^ Qt::MetaModifier;
593
594 // detect numeric keypad keys
595 if (chm.vkey == VK_ENTER || chm.vkey == VK_NUMLOCK) {
596 // these always come from the numpad
597 state |= Qt::KeypadModifier;
598 } else if (((chm.vkey >= VK_PAGEUP && chm.vkey <= VK_DOWN) ||
599 chm.vkey == VK_INSERT || chm.vkey == VK_DELETE)) {
600 if ((chm.chr & 0xFF) != 0xE0) {
601 state |= Qt::KeypadModifier;
602 if ((state & Qt::AltModifier) && chm.vkey != VK_DELETE) {
603 // hide the key from Qt widgets and let the standard OS/2 Alt+ddd
604 // shortcut (that composed chars from typed in ASCII codes) work
605 // correctly. If we don't do that, widgets will see both individual
606 // digits (or cursor movements if NumLock is off) and the char
607 // composed by Alt+ddd.
608 return false;
609 }
610 }
611 }
612 // detect other numpad keys. OS/2 doesn't assign virtual keys to them
613 // so use scancodes (it can be device-dependent, is there a better way?)
614 switch (chm.scancode) {
615 case 0x4C: // 5
616 state |= Qt::KeypadModifier;
617 if (state & Qt::AltModifier) {
618 // hide the key from Qt (see above)
619 return false;
620 } else {
621 // scancode is zero if Numlock is set
622 if (!code)
623 code = Qt::Key_Clear;
624 }
625 break;
626 case 0x37: // *
627 // OS/2 assigns VK_PRINTSCRN to it when pressed with Shift, also
628 // it sets chr to zero when it is released with Alt or Ctrl
629 // leaving vkey as zero too, and does few other strange things --
630 // override them all
631 code = Qt::Key_Asterisk;
632 state |= Qt::KeypadModifier;
633 break;
634 case 0x5C: // /
635 code = Qt::Key_Slash;
636 // fall through
637 case 0x4A: // -
638 case 0x4E: // +
639 // the code for the above two is obtained by KbdXlate above
640 state |= Qt::KeypadModifier;
641 break;
642 }
643
644 if ((state & Qt::KeypadModifier) && (chm.fs & KC_CHAR) &&
645 chm.vkey != VK_ENTER) {
646 // if it's a numpad key and it also provides a character, it means
647 // Numlock is on. According to other platforms, key code must be the
648 // character value in this case. Reset code to 0 to have it done so.
649 // Enter is an exception as it should always produce a virtual code.
650 if (chm.fs & KC_CHAR)
651 code = 0;
652 }
653
654 // Note: code and/or chm.scancode may be zero here. We cannot ignore such
655 // events because, for example, all non-ASCII letters have zero virtual
656 // codes, and DBCS characters entered via IME have both zero virtual codes
657 // and zero scancodes. However, if both code and chm.scancode are zero
658 // (as for DBCS), storeKey()/findKey() will do nothing which means that:
659 //
660 // 1) QKeyEvents will not have the auto-repeat flag set when a key is
661 // being auto-repeated by the system;
662 // 2) there will be no QEvent::KeyRelease event corresponding to the
663 // QEvent::KeyPress event.
664 //
665 // This seems to be acceptable.
666
667 // KEYDOWN -----------------------------------------------------------------
668 if (!(chm.fs & KC_KEYUP)) {
669 // Get the last record of this key press, so we can validate the current state
670 // The record is not removed from the list
671 KeyRecord *rec = key_recorder.findKey(chm.scancode, false);
672
673 if (state == Qt::AltModifier) {
674 // Special handling of global PM hotkeys
675 switch (code) {
676 case Qt::Key_Space: {
677 // show system menu
678 HWND fId = widget->window()->internalFrameWinId();
679 HWND sysMenu = WinWindowFromID(fId, FID_SYSMENU);
680 if (!sysMenu)
681 break; // no menu for this window
682 WinPostMsg(sysMenu, MM_STARTMENUMODE,
683 MPFROM2SHORT(TRUE, FALSE), 0);
684 return true;
685 }
686 case Qt::Key_F4: {
687 // we handle this key combination ourselves because not
688 // all top-level widgets have the system menu
689 WinPostMsg(widget->window()->internalFrameWinId(),
690 WM_CLOSE, 0, 0);
691 return true;
692 }
693 default:
694 break;
695 }
696 }
697
698 // If rec's state doesn't match the current state, something has changed behind our back
699 // (Consumed by modal widget is one possibility) So, remove the record from the list
700 // This will stop the auto-repeat of the key, should a modifier change, for example
701 if (rec && rec->state != state) {
702 key_recorder.findKey(chm.scancode, true);
703 rec = 0;
704 }
705
706 // If we have a record, it means that the key is already pressed, the state is the same
707 // so, we have an auto-repeating key
708 if (rec) {
709 Q_ASSERT(!code || code == rec->code);
710 if (rec->code < Qt::Key_Shift || rec->code > Qt::Key_ScrollLock) {
711 k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code,
712 Qt::KeyboardModifier(state), rec->text, true, 0,
713 chm.scancode, chm.vkey, nativeMods);
714 k1 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, rec->code,
715 Qt::KeyboardModifier(state), rec->text, true, 0,
716 chm.scancode, chm.vkey, nativeMods);
717 }
718 }
719 // No record of the key being previous pressed, so we now send a QEvent::KeyPress event,
720 // and store the key data into our records.
721 else {
722 QString text;
723 // we assume that we have a correct 8-bit character not only when
724 // KC_CHAR is set but also when there are one or more modifiers
725 // (in which case KC_CHAR is not present) but no KC_VIRTUALKEY.
726 // The latter is in particular important for Alt+Letter combos
727 // processing Alt+Letter shortcuts for non-ASCII letters in widgets
728 // (e.g. QPushButton) depends on that.
729 if (((chm.fs & KC_CHAR) ||
730 (!(chm.fs & KC_VIRTUALKEY) && (chm.fs & (KC_CTRL | KC_ALT | KC_SHIFT)))) &&
731 chm.chr) {
732 if (chm.chr & 0xFF00) {
733 // We assume we get a DBCS char if the above condition is met.
734 text = QString::fromLocal8Bit((char *)&chm.chr, 2);
735 } else {
736 text = QString::fromLocal8Bit((char*)&chm.chr, 1);
737 }
738
739 if (chm.fs & KC_DEADKEY) {
740 // convert dead keys to Key_Dead_* codes and set text to
741 // null to avoid interpreting them as normal chars
742 Q_ASSERT(text.size() == 1);
743 code = deadCharToDeadKeyCode(text[0]);
744 text = QString();
745 } else if (chm.fs & KC_INVALIDCOMP) {
746 // if the pressed letter is invalid for the given dead
747 // key, we set text to null as well (to meet the PM
748 // behavior) and set the code to the uppercase unicode
749 // value (similar to other cases) to have it recognized
750 Q_ASSERT(text.size() == 1);
751 code = text[0].toUpper().unicode();
752 text = QString();
753 }
754
755 Q_ASSERT(code || !text.isEmpty()); // we must provide the key code
756 if (!code && !text.isEmpty()) {
757 code = text[0].toUpper().unicode();
758 }
759
760 if ((state & Qt::ControlModifier) &&
761 !(state & Qt::KeypadModifier) && !(state & Qt::AltModifier) &&
762 !text.isEmpty() && !text[0].row()) {
763 // Ctrl + A..Z etc. produce ascii from 0x01 to 0x1F
764 int ascii = text[0].toUpper().cell();
765 if (ascii >= 0x41 && ascii <= 0x5F)
766 ascii -= 0x40;
767 // the below emulates OS/2 functionality. It differs from
768 // e.g. Win32.
769 else if (ascii == 0x36 && !(state & Qt::KeypadModifier))
770 ascii = 0x1E;
771 else if (ascii == 0x2D)
772 ascii = 0x1F;
773 else if (ascii >= 0x7B && ascii <= 0x7D)
774 ascii -= 0x60;
775 text = QChar(ascii);
776 }
777 }
778 key_recorder.storeKey(chm.scancode, code, state, text);
779 k0 = q->sendKeyEvent(widget, grab, QEvent::KeyPress, code,
780 Qt::KeyboardModifier(state), text, false, 0,
781 chm.scancode, chm.vkey, nativeMods);
782 }
783 }
784 // KEYUP -------------------------------------------------------------------
785 else {
786 // Try to locate the key in our records, and remove it if it exists.
787 // The key may not be in our records if, for example, the down event was handled by
788 // PM natively, or our window gets focus while a key is already press, but now gets
789 // the key release event.
790 KeyRecord* rec = key_recorder.findKey(chm.scancode, true);
791 if (!rec) {
792 // Someone ate the key down event
793 } else {
794 k0 = q->sendKeyEvent(widget, grab, QEvent::KeyRelease, rec->code,
795 Qt::KeyboardModifier(state), rec->text, false, 0,
796 chm.scancode, chm.vkey, nativeMods);
797 }
798 }
799
800 // Return true, if a QKeyEvent was sent to a widget
801 return k0 || k1;
802}
803
804// QKeyMapper (PM) implementation ----------------------------------[ start ]---
805
806bool QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
807 QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
808 const QString &text, bool autorepeat, int count,
809 quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
810 bool *)
811{
812 Q_UNUSED(count);
813
814#if defined QT3_SUPPORT && !defined(QT_NO_SHORTCUT)
815 if (type == QEvent::KeyPress
816 && !grab
817 && QApplicationPrivate::instance()->use_compat()) {
818 // send accel events if the keyboard is not grabbed
819 QKeyEventEx a(type, code, modifiers,
820 text, autorepeat, qMax(1, int(text.length())),
821 nativeScanCode, nativeVirtualKey, nativeModifiers);
822 if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &a))
823 return true;
824 }
825#endif
826 if (!widget->isEnabled())
827 return false;
828
829 QKeyEventEx e(type, code, modifiers,
830 text, autorepeat, qMax(1, int(text.length())),
831 nativeScanCode, nativeVirtualKey, nativeModifiers);
832 QETWidget::sendSpontaneousEvent(widget, &e);
833
834 return e.isAccepted();
835}
836
837QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.