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

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

gui: Fixed: Ctrl+A..Z should generate characters with codes 0x01-0x1F.

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