source: trunk/src/gui/kernel/qkeymapper_mac.cpp@ 744

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

trunk: Merged in qt 4.6.2 sources.

File size: 37.6 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** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <private/qt_mac_p.h>
43#include <qdebug.h>
44#include <qevent.h>
45#include <private/qevent_p.h>
46#include <qtextcodec.h>
47#include <qapplication.h>
48#include <qinputcontext.h>
49#include <private/qkeymapper_p.h>
50#include <private/qapplication_p.h>
51#include <private/qmacinputcontext_p.h>
52
53QT_BEGIN_NAMESPACE
54
55QT_USE_NAMESPACE
56
57/*****************************************************************************
58 QKeyMapper debug facilities
59 *****************************************************************************/
60//#define DEBUG_KEY_BINDINGS
61//#define DEBUG_KEY_BINDINGS_MODIFIERS
62//#define DEBUG_KEY_MAPS
63
64/*****************************************************************************
65 Internal variables and functions
66 *****************************************************************************/
67bool qt_mac_eat_unicode_key = false;
68extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); //qapplication_mac.cpp
69
70Q_GUI_EXPORT void qt_mac_secure_keyboard(bool b)
71{
72 static bool secure = false;
73 if (b != secure){
74 b ? EnableSecureEventInput() : DisableSecureEventInput();
75 secure = b;
76 }
77}
78
79/*
80 \internal
81 A Mac KeyboardLayoutItem has 8 possible states:
82 1. Unmodified
83 2. Shift
84 3. Control
85 4. Control + Shift
86 5. Alt
87 6. Alt + Shift
88 7. Alt + Control
89 8. Alt + Control + Shift
90 9. Meta
91 10. Meta + Shift
92 11. Meta + Control
93 12. Meta + Control + Shift
94 13. Meta + Alt
95 14. Meta + Alt + Shift
96 15. Meta + Alt + Control
97 16. Meta + Alt + Control + Shift
98*/
99struct KeyboardLayoutItem {
100 bool dirty;
101 quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character
102};
103
104// Possible modifier states.
105// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()!
106static const Qt::KeyboardModifiers ModsTbl[] = {
107 Qt::NoModifier, // 0
108 Qt::ShiftModifier, // 1
109 Qt::ControlModifier, // 2
110 Qt::ControlModifier | Qt::ShiftModifier, // 3
111 Qt::AltModifier, // 4
112 Qt::AltModifier | Qt::ShiftModifier, // 5
113 Qt::AltModifier | Qt::ControlModifier, // 6
114 Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
115 Qt::MetaModifier, // 8
116 Qt::MetaModifier | Qt::ShiftModifier, // 9
117 Qt::MetaModifier | Qt::ControlModifier, // 10
118 Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier,// 11
119 Qt::MetaModifier | Qt::AltModifier, // 12
120 Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier, // 13
121 Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier, // 14
122 Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 15
123};
124
125/* key maps */
126struct qt_mac_enum_mapper
127{
128 int mac_code;
129 int qt_code;
130#if defined(DEBUG_KEY_BINDINGS)
131# define QT_MAC_MAP_ENUM(x) x, #x
132 const char *desc;
133#else
134# define QT_MAC_MAP_ENUM(x) x
135#endif
136};
137
138//modifiers
139static qt_mac_enum_mapper qt_mac_modifier_symbols[] = {
140 { shiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
141 { rightShiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
142 { controlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
143 { rightControlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
144 { cmdKey, QT_MAC_MAP_ENUM(Qt::ControlModifier) },
145 { optionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
146 { rightOptionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
147 { kEventKeyModifierNumLockMask, QT_MAC_MAP_ENUM(Qt::KeypadModifier) },
148 { 0, QT_MAC_MAP_ENUM(0) }
149};
150Qt::KeyboardModifiers qt_mac_get_modifiers(int keys)
151{
152#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
153 qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", keys, keys);
154#endif
155 Qt::KeyboardModifiers ret = Qt::NoModifier;
156 for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
157 if (keys & qt_mac_modifier_symbols[i].mac_code) {
158#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
159 qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
160#endif
161 ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code);
162 }
163 }
164 if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
165 Qt::KeyboardModifiers oldModifiers = ret;
166 ret &= ~(Qt::MetaModifier | Qt::ControlModifier);
167 if (oldModifiers & Qt::ControlModifier)
168 ret |= Qt::MetaModifier;
169 if (oldModifiers & Qt::MetaModifier)
170 ret |= Qt::ControlModifier;
171 }
172 return ret;
173}
174static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys)
175{
176#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
177 qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", (int)keys, (int)keys);
178#endif
179 int ret = 0;
180 for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
181 if (keys & qt_mac_modifier_symbols[i].qt_code) {
182#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
183 qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
184#endif
185 ret |= qt_mac_modifier_symbols[i].mac_code;
186 }
187 }
188
189 if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
190 int oldModifiers = ret;
191 ret &= ~(controlKeyBit | cmdKeyBit);
192 if (oldModifiers & controlKeyBit)
193 ret |= cmdKeyBit;
194 if (oldModifiers & cmdKeyBit)
195 ret |= controlKeyBit;
196 }
197 return ret;
198}
199void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object)
200{
201 static quint32 cachedModifiers = 0;
202 quint32 lastModifiers = cachedModifiers,
203 changedModifiers = lastModifiers ^ modifiers;
204 cachedModifiers = modifiers;
205
206 //check the bits
207 static qt_mac_enum_mapper modifier_key_symbols[] = {
208 { shiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) },
209 { rightShiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) }, //???
210 { controlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) },
211 { rightControlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) }, //???
212 { cmdKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Control) },
213 { optionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) },
214 { rightOptionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) }, //???
215 { alphaLockBit, QT_MAC_MAP_ENUM(Qt::Key_CapsLock) },
216 { kEventKeyModifierNumLockBit, QT_MAC_MAP_ENUM(Qt::Key_NumLock) },
217 { 0, QT_MAC_MAP_ENUM(0) } };
218 for (int i = 0; i <= 32; i++) { //just check each bit
219 if (!(changedModifiers & (1 << i)))
220 continue;
221 QEvent::Type etype = QEvent::KeyPress;
222 if (lastModifiers & (1 << i))
223 etype = QEvent::KeyRelease;
224 int key = 0;
225 for (uint x = 0; modifier_key_symbols[x].mac_code; x++) {
226 if (modifier_key_symbols[x].mac_code == i) {
227#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
228 qDebug("got modifier changed: %s", modifier_key_symbols[x].desc);
229#endif
230 key = modifier_key_symbols[x].qt_code;
231 break;
232 }
233 }
234 if (!key) {
235#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
236 qDebug("could not get modifier changed: %d", i);
237#endif
238 continue;
239 }
240#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
241 qDebug("KeyEvent (modif): Sending %s to %s::%s: %d - 0x%08x",
242 etype == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
243 object ? object->metaObject()->className() : "none",
244 object ? object->objectName().toLatin1().constData() : "",
245 key, (int)modifiers);
246#endif
247 QKeyEvent ke(etype, key, qt_mac_get_modifiers(modifiers ^ (1 << i)), QLatin1String(""));
248 qt_sendSpontaneousEvent(object, &ke);
249 }
250}
251
252//keyboard keys (non-modifiers)
253static qt_mac_enum_mapper qt_mac_keyboard_symbols[] = {
254 { kHomeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Home) },
255 { kEnterCharCode, QT_MAC_MAP_ENUM(Qt::Key_Enter) },
256 { kEndCharCode, QT_MAC_MAP_ENUM(Qt::Key_End) },
257 { kBackspaceCharCode, QT_MAC_MAP_ENUM(Qt::Key_Backspace) },
258 { kTabCharCode, QT_MAC_MAP_ENUM(Qt::Key_Tab) },
259 { kPageUpCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageUp) },
260 { kPageDownCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageDown) },
261 { kReturnCharCode, QT_MAC_MAP_ENUM(Qt::Key_Return) },
262 { kEscapeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Escape) },
263 { kLeftArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Left) },
264 { kRightArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Right) },
265 { kUpArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Up) },
266 { kDownArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Down) },
267 { kHelpCharCode, QT_MAC_MAP_ENUM(Qt::Key_Help) },
268 { kDeleteCharCode, QT_MAC_MAP_ENUM(Qt::Key_Delete) },
269//ascii maps, for debug
270 { ':', QT_MAC_MAP_ENUM(Qt::Key_Colon) },
271 { ';', QT_MAC_MAP_ENUM(Qt::Key_Semicolon) },
272 { '<', QT_MAC_MAP_ENUM(Qt::Key_Less) },
273 { '=', QT_MAC_MAP_ENUM(Qt::Key_Equal) },
274 { '>', QT_MAC_MAP_ENUM(Qt::Key_Greater) },
275 { '?', QT_MAC_MAP_ENUM(Qt::Key_Question) },
276 { '@', QT_MAC_MAP_ENUM(Qt::Key_At) },
277 { ' ', QT_MAC_MAP_ENUM(Qt::Key_Space) },
278 { '!', QT_MAC_MAP_ENUM(Qt::Key_Exclam) },
279 { '"', QT_MAC_MAP_ENUM(Qt::Key_QuoteDbl) },
280 { '#', QT_MAC_MAP_ENUM(Qt::Key_NumberSign) },
281 { '$', QT_MAC_MAP_ENUM(Qt::Key_Dollar) },
282 { '%', QT_MAC_MAP_ENUM(Qt::Key_Percent) },
283 { '&', QT_MAC_MAP_ENUM(Qt::Key_Ampersand) },
284 { '\'', QT_MAC_MAP_ENUM(Qt::Key_Apostrophe) },
285 { '(', QT_MAC_MAP_ENUM(Qt::Key_ParenLeft) },
286 { ')', QT_MAC_MAP_ENUM(Qt::Key_ParenRight) },
287 { '*', QT_MAC_MAP_ENUM(Qt::Key_Asterisk) },
288 { '+', QT_MAC_MAP_ENUM(Qt::Key_Plus) },
289 { ',', QT_MAC_MAP_ENUM(Qt::Key_Comma) },
290 { '-', QT_MAC_MAP_ENUM(Qt::Key_Minus) },
291 { '.', QT_MAC_MAP_ENUM(Qt::Key_Period) },
292 { '/', QT_MAC_MAP_ENUM(Qt::Key_Slash) },
293 { '[', QT_MAC_MAP_ENUM(Qt::Key_BracketLeft) },
294 { ']', QT_MAC_MAP_ENUM(Qt::Key_BracketRight) },
295 { '\\', QT_MAC_MAP_ENUM(Qt::Key_Backslash) },
296 { '_', QT_MAC_MAP_ENUM(Qt::Key_Underscore) },
297 { '`', QT_MAC_MAP_ENUM(Qt::Key_QuoteLeft) },
298 { '{', QT_MAC_MAP_ENUM(Qt::Key_BraceLeft) },
299 { '}', QT_MAC_MAP_ENUM(Qt::Key_BraceRight) },
300 { '|', QT_MAC_MAP_ENUM(Qt::Key_Bar) },
301 { '~', QT_MAC_MAP_ENUM(Qt::Key_AsciiTilde) },
302 { '^', QT_MAC_MAP_ENUM(Qt::Key_AsciiCircum) },
303 { 0, QT_MAC_MAP_ENUM(0) }
304};
305
306static qt_mac_enum_mapper qt_mac_keyvkey_symbols[] = { //real scan codes
307 { 122, QT_MAC_MAP_ENUM(Qt::Key_F1) },
308 { 120, QT_MAC_MAP_ENUM(Qt::Key_F2) },
309 { 99, QT_MAC_MAP_ENUM(Qt::Key_F3) },
310 { 118, QT_MAC_MAP_ENUM(Qt::Key_F4) },
311 { 96, QT_MAC_MAP_ENUM(Qt::Key_F5) },
312 { 97, QT_MAC_MAP_ENUM(Qt::Key_F6) },
313 { 98, QT_MAC_MAP_ENUM(Qt::Key_F7) },
314 { 100, QT_MAC_MAP_ENUM(Qt::Key_F8) },
315 { 101, QT_MAC_MAP_ENUM(Qt::Key_F9) },
316 { 109, QT_MAC_MAP_ENUM(Qt::Key_F10) },
317 { 103, QT_MAC_MAP_ENUM(Qt::Key_F11) },
318 { 111, QT_MAC_MAP_ENUM(Qt::Key_F12) },
319 { 105, QT_MAC_MAP_ENUM(Qt::Key_F13) },
320 { 107, QT_MAC_MAP_ENUM(Qt::Key_F14) },
321 { 113, QT_MAC_MAP_ENUM(Qt::Key_F15) },
322 { 106, QT_MAC_MAP_ENUM(Qt::Key_F16) },
323 { 0, QT_MAC_MAP_ENUM(0) }
324};
325
326static int qt_mac_get_key(int modif, const QChar &key, int virtualKey)
327{
328#ifdef DEBUG_KEY_BINDINGS
329 qDebug("**Mapping key: %d (0x%04x) - %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey);
330#endif
331
332 if (key == kClearCharCode && virtualKey == 0x47)
333 return Qt::Key_Clear;
334
335 if (key.isDigit()) {
336#ifdef DEBUG_KEY_BINDINGS
337 qDebug("%d: got key: %d", __LINE__, key.digitValue());
338#endif
339 return key.digitValue() + Qt::Key_0;
340 }
341
342 if (key.isLetter()) {
343#ifdef DEBUG_KEY_BINDINGS
344 qDebug("%d: got key: %d", __LINE__, (key.toUpper().unicode() - 'A'));
345#endif
346 return (key.toUpper().unicode() - 'A') + Qt::Key_A;
347 }
348 if (key.isSymbol()) {
349#ifdef DEBUG_KEY_BINDINGS
350 qDebug("%d: got key: %d", __LINE__, (key.unicode()));
351#endif
352 return key.unicode();
353 }
354
355 for (int i = 0; qt_mac_keyboard_symbols[i].qt_code; i++) {
356 if (qt_mac_keyboard_symbols[i].mac_code == key) {
357 /* To work like Qt for X11 we issue Backtab when Shift + Tab are pressed */
358 if (qt_mac_keyboard_symbols[i].qt_code == Qt::Key_Tab && (modif & Qt::ShiftModifier)) {
359#ifdef DEBUG_KEY_BINDINGS
360 qDebug("%d: got key: Qt::Key_Backtab", __LINE__);
361#endif
362 return Qt::Key_Backtab;
363 }
364
365#ifdef DEBUG_KEY_BINDINGS
366 qDebug("%d: got key: %s", __LINE__, qt_mac_keyboard_symbols[i].desc);
367#endif
368 return qt_mac_keyboard_symbols[i].qt_code;
369 }
370 }
371
372 //last ditch try to match the scan code
373 for (int i = 0; qt_mac_keyvkey_symbols[i].qt_code; i++) {
374 if (qt_mac_keyvkey_symbols[i].mac_code == virtualKey) {
375#ifdef DEBUG_KEY_BINDINGS
376 qDebug("%d: got key: %s", __LINE__, qt_mac_keyvkey_symbols[i].desc);
377#endif
378 return qt_mac_keyvkey_symbols[i].qt_code;
379 }
380 }
381
382 //oh well
383#ifdef DEBUG_KEY_BINDINGS
384 qDebug("Unknown case.. %s:%d %d[%d] %d", __FILE__, __LINE__, key.unicode(), key.toLatin1(), virtualKey);
385#endif
386 return Qt::Key_unknown;
387}
388
389static Boolean qt_KeyEventComparatorProc(EventRef inEvent, void *data)
390{
391 UInt32 ekind = GetEventKind(inEvent),
392 eclass = GetEventClass(inEvent);
393 return (eclass == kEventClassKeyboard && (void *)ekind == data);
394}
395
396static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, int *qtKey,
397 QChar *outChar, Qt::KeyboardModifiers *outModifiers, bool *outHandled)
398{
399#if !defined(QT_MAC_USE_COCOA) || defined(Q_OS_MAC64)
400 Q_UNUSED(er);
401 Q_UNUSED(outHandled);
402#endif
403 const UInt32 ekind = GetEventKind(keyEvent);
404 {
405 UInt32 mac_modifiers = 0;
406 GetEventParameter(keyEvent, kEventParamKeyModifiers, typeUInt32, 0,
407 sizeof(mac_modifiers), 0, &mac_modifiers);
408#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
409 qDebug("************ Mapping modifiers and key ***********");
410#endif
411 *outModifiers = qt_mac_get_modifiers(mac_modifiers);
412#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
413 qDebug("------------ Mapping modifiers and key -----------");
414#endif
415 }
416
417 //get keycode
418 UInt32 keyCode = 0;
419 GetEventParameter(keyEvent, kEventParamKeyCode, typeUInt32, 0, sizeof(keyCode), 0, &keyCode);
420
421 //get mac mapping
422 static UInt32 tmp_unused_state = 0L;
423 const UCKeyboardLayout *uchrData = 0;
424#if defined(Q_OS_MAC32)
425 KeyboardLayoutRef keyLayoutRef = 0;
426 KLGetCurrentKeyboardLayout(&keyLayoutRef);
427 OSStatus err;
428 if (keyLayoutRef != 0) {
429 err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
430 (reinterpret_cast<const void **>(&uchrData)));
431 if (err != noErr) {
432 qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d",
433 long(err), __FILE__, __LINE__);
434 }
435 }
436#else
437 QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource();
438 Q_ASSERT(inputSource != 0);
439 CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource,
440 kTISPropertyUnicodeKeyLayoutData));
441 uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
442#endif
443 *qtKey = Qt::Key_unknown;
444 if (uchrData) {
445 // The easy stuff; use the unicode stuff!
446 UniChar string[4];
447 UniCharCount actualLength;
448 UInt32 currentModifiers = GetCurrentEventKeyModifiers();
449 UInt32 currentModifiersWOAltOrControl = currentModifiers & ~(controlKey | optionKey);
450 int keyAction;
451 switch (ekind) {
452 default:
453 case kEventRawKeyDown:
454 keyAction = kUCKeyActionDown;
455 break;
456 case kEventRawKeyUp:
457 keyAction = kUCKeyActionUp;
458 break;
459 case kEventRawKeyRepeat:
460 keyAction = kUCKeyActionAutoKey;
461 break;
462 }
463 OSStatus err = UCKeyTranslate(uchrData, keyCode, keyAction,
464 ((currentModifiersWOAltOrControl >> 8) & 0xff), LMGetKbdType(),
465 kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
466 string);
467 if (err == noErr) {
468 *outChar = QChar(string[0]);
469 *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
470 if (currentModifiersWOAltOrControl != currentModifiers) {
471 // Now get the real char.
472 err = UCKeyTranslate(uchrData, keyCode, keyAction,
473 ((currentModifiers >> 8) & 0xff), LMGetKbdType(),
474 kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
475 string);
476 if (err == noErr)
477 *outChar = QChar(string[0]);
478 }
479 } else {
480 qWarning("Qt::internal::UCKeyTranslate is returnining %ld %s:%d",
481 long(err), __FILE__, __LINE__);
482 }
483 }
484#ifdef Q_OS_MAC32
485 else {
486 // The road less travelled; use KeyTranslate
487 const void *keyboard_layout;
488 KeyboardLayoutRef keyLayoutRef = 0;
489 KLGetCurrentKeyboardLayout(&keyLayoutRef);
490 err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
491 reinterpret_cast<const void **>(&keyboard_layout));
492
493 int translatedChar = KeyTranslate(keyboard_layout, (GetCurrentEventKeyModifiers() &
494 (kEventKeyModifierNumLockMask|shiftKey|cmdKey|
495 rightShiftKey|alphaLock)) | keyCode,
496 &tmp_unused_state);
497 if (!translatedChar) {
498#ifdef QT_MAC_USE_COCOA
499 if (outHandled) {
500 qt_mac_eat_unicode_key = false;
501 if (er)
502 CallNextEventHandler(er, keyEvent);
503 *outHandled = qt_mac_eat_unicode_key;
504 }
505#endif
506 return false;
507 }
508
509 //map it into qt keys
510 *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
511 if (*outModifiers & (Qt::AltModifier | Qt::ControlModifier)) {
512 if (translatedChar & (1 << 7)) //high ascii
513 translatedChar = 0;
514 } else { //now get the real ascii value
515 UInt32 tmp_mod = 0L;
516 static UInt32 tmp_state = 0L;
517 if (*outModifiers & Qt::ShiftModifier)
518 tmp_mod |= shiftKey;
519 if (*outModifiers & Qt::MetaModifier)
520 tmp_mod |= controlKey;
521 if (*outModifiers & Qt::ControlModifier)
522 tmp_mod |= cmdKey;
523 if (GetCurrentEventKeyModifiers() & alphaLock) //no Qt mapper
524 tmp_mod |= alphaLock;
525 if (*outModifiers & Qt::AltModifier)
526 tmp_mod |= optionKey;
527 if (*outModifiers & Qt::KeypadModifier)
528 tmp_mod |= kEventKeyModifierNumLockMask;
529 translatedChar = KeyTranslate(keyboard_layout, tmp_mod | keyCode, &tmp_state);
530 }
531 {
532 ByteCount unilen = 0;
533 if (GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0)
534 == noErr && unilen == 2) {
535 GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, outChar);
536 } else if (translatedChar) {
537 static QTextCodec *c = 0;
538 if (!c)
539 c = QTextCodec::codecForName("Apple Roman");
540 char tmpChar = (char)translatedChar; // **sigh**
541 *outChar = c->toUnicode(&tmpChar, 1).at(0);
542 } else {
543 *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
544 }
545 }
546 }
547#endif
548 if (*qtKey == Qt::Key_unknown)
549 *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
550 return true;
551}
552
553QKeyMapperPrivate::QKeyMapperPrivate()
554{
555 memset(keyLayout, 0, sizeof(keyLayout));
556 keyboard_layout_format.unicode = 0;
557#ifdef Q_OS_MAC32
558 keyboard_mode = NullMode;
559#else
560 currentInputSource = 0;
561#endif
562}
563
564QKeyMapperPrivate::~QKeyMapperPrivate()
565{
566 deleteLayouts();
567}
568
569bool
570QKeyMapperPrivate::updateKeyboard()
571{
572 const UCKeyboardLayout *uchrData = 0;
573#ifdef Q_OS_MAC32
574 KeyboardLayoutRef keyLayoutRef = 0;
575 KLGetCurrentKeyboardLayout(&keyLayoutRef);
576
577 if (keyboard_mode != NullMode && currentKeyboardLayout == keyLayoutRef)
578 return false;
579
580 OSStatus err;
581 if (keyLayoutRef != 0) {
582 err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
583 const_cast<const void **>(reinterpret_cast<const void **>(&uchrData)));
584 if (err != noErr) {
585 qWarning("Qt::internal::unable to get unicode keyboardlayout %ld %s:%d",
586 long(err), __FILE__, __LINE__);
587 }
588 }
589#else
590 QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
591 if (keyboard_mode != NullMode && source == currentInputSource) {
592 return false;
593 }
594 Q_ASSERT(source != 0);
595 CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(source,
596 kTISPropertyUnicodeKeyLayoutData));
597 uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
598#endif
599
600 keyboard_kind = LMGetKbdType();
601 if (uchrData) {
602 keyboard_layout_format.unicode = uchrData;
603 keyboard_mode = UnicodeMode;
604 }
605#ifdef Q_OS_MAC32
606 else {
607 void *happy;
608 err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
609 const_cast<const void **>(reinterpret_cast<void **>(&happy)));
610 if (err != noErr) {
611 qFatal("Qt::internal::unable to get non-unicode layout, cannot procede %ld %s:%d",
612 long(err), __FILE__, __LINE__);
613 }
614 keyboard_layout_format.other = happy;
615 keyboard_mode = OtherMode;
616 }
617
618 currentKeyboardLayout = keyLayoutRef;
619#else
620 currentInputSource = source;
621#endif
622 keyboard_dead = 0;
623 CFStringRef iso639Code;
624#ifdef Q_OS_MAC32
625# ifndef kKLLanguageCode
626# define kKLLanguageCode 9
627# endif
628 KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLLanguageCode,
629 reinterpret_cast<const void **>(&iso639Code));
630#else
631 CFArrayRef array = static_cast<CFArrayRef>(TISGetInputSourceProperty(currentInputSource, kTISPropertyInputSourceLanguages));
632 iso639Code = static_cast<CFStringRef>(CFArrayGetValueAtIndex(array, 0)); // Actually a RFC3066bis, but it's close enough
633#endif
634 if (iso639Code) {
635 keyboardInputLocale = QLocale(QCFString::toQString(iso639Code));
636 QString monday = keyboardInputLocale.dayName(1);
637 bool rtl = false;
638 for (int i = 0; i < monday.length(); ++i) {
639 switch (monday.at(i).direction()) {
640 default:
641 break;
642 case QChar::DirR:
643 case QChar::DirAL:
644 case QChar::DirRLE:
645 case QChar::DirRLO:
646 rtl = true;
647 break;
648 }
649 if (rtl)
650 break;
651 }
652 keyboardInputDirection = rtl ? Qt::RightToLeft : Qt::LeftToRight;
653 } else {
654 keyboardInputLocale = QLocale::c();
655 keyboardInputDirection = Qt::LeftToRight;
656 }
657 return true;
658}
659
660void
661QKeyMapperPrivate::deleteLayouts()
662{
663 keyboard_mode = NullMode;
664 for (int i = 0; i < 255; ++i) {
665 if (keyLayout[i]) {
666 delete keyLayout[i];
667 keyLayout[i] = 0;
668 }
669 }
670}
671
672void
673QKeyMapperPrivate::clearMappings()
674{
675 deleteLayouts();
676 updateKeyboard();
677}
678
679QList<int>
680QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
681{
682 QList<int> ret;
683
684 KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()];
685 if (!kbItem) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard)
686 return ret;
687
688 int baseKey = kbItem->qtKey[0];
689 Qt::KeyboardModifiers keyMods = e->modifiers();
690 ret << int(baseKey + keyMods); // The base key is _always_ valid, of course
691
692 for (int i = 1; i < 8; ++i) {
693 Qt::KeyboardModifiers neededMods = ModsTbl[i];
694 int key = kbItem->qtKey[i];
695 if (key && key != baseKey && ((keyMods & neededMods) == neededMods))
696 ret << int(key + (keyMods & ~neededMods));
697 }
698
699 return ret;
700}
701
702bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef er, EventRef event,
703 void *info, bool grab)
704{
705 Q_ASSERT(GetEventClass(event) == kEventClassKeyboard);
706 bool handled_event=true;
707 UInt32 ekind = GetEventKind(event);
708
709 // unfortunately modifiers changed event looks quite different, so I have a separate
710 // code path
711 if (ekind == kEventRawKeyModifiersChanged) {
712 //figure out changed modifiers, wish Apple would just send a delta
713 UInt32 modifiers = 0;
714 GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
715 sizeof(modifiers), 0, &modifiers);
716 qt_mac_send_modifiers_changed(modifiers, widget);
717 return true;
718 }
719
720 if (qApp->inputContext() && qApp->inputContext()->isComposing()) {
721 if (ekind == kEventRawKeyDown) {
722 QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext());
723 if (context)
724 context->setLastKeydownEvent(event);
725 }
726 return false;
727 }
728 //get modifiers
729 Qt::KeyboardModifiers modifiers;
730 int qtKey;
731 QChar ourChar;
732 if (translateKeyEventInternal(er, event, &qtKey, &ourChar, &modifiers,
733 &handled_event) == false)
734 return handled_event;
735 QString text(ourChar);
736 /* This is actually wrong - but unfortunatly it is the best that can be
737 done for now because of the Control/Meta mapping problems */
738 if (modifiers & (Qt::ControlModifier | Qt::MetaModifier)
739 && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
740 text = QString();
741 }
742
743
744 if (widget) {
745#ifndef QT_MAC_USE_COCOA
746 Q_UNUSED(info);
747 // Try not to call "other" event handlers if we have a popup,
748 // However, if the key has text
749 // then we should pass it along because otherwise then people
750 // can use input method stuff.
751 if (!qApp->activePopupWidget()
752 || (qApp->activePopupWidget() && !text.isEmpty())) {
753 //Find out if someone else wants the event, namely
754 //is it of use to text services? If so we won't bother
755 //with a QKeyEvent.
756 qt_mac_eat_unicode_key = false;
757 if (er)
758 CallNextEventHandler(er, event);
759 extern bool qt_mac_menubar_is_open();
760 if (qt_mac_eat_unicode_key || qt_mac_menubar_is_open()) {
761 return true;
762 }
763 }
764#endif
765 // Try to compress key events.
766 if (!text.isEmpty() && widget->testAttribute(Qt::WA_KeyCompression)) {
767 EventTime lastTime = GetEventTime(event);
768 for (;;) {
769 EventRef releaseEvent = FindSpecificEventInQueue(GetMainEventQueue(),
770 qt_KeyEventComparatorProc,
771 (void*)kEventRawKeyUp);
772 if (!releaseEvent)
773 break;
774 const EventTime releaseTime = GetEventTime(releaseEvent);
775 if (releaseTime < lastTime)
776 break;
777 lastTime = releaseTime;
778
779 EventRef pressEvent = FindSpecificEventInQueue(GetMainEventQueue(),
780 qt_KeyEventComparatorProc,
781 (void*)kEventRawKeyDown);
782 if (!pressEvent)
783 break;
784 const EventTime pressTime = GetEventTime(pressEvent);
785 if (pressTime < lastTime)
786 break;
787 lastTime = pressTime;
788
789 Qt::KeyboardModifiers compressMod;
790 int compressQtKey = 0;
791 QChar compressChar;
792 if (translateKeyEventInternal(er, pressEvent,
793 &compressQtKey, &compressChar, &compressMod, 0)
794 == false) {
795 break;
796 }
797 // Copied from qapplication_x11.cpp (change both).
798
799 bool stopCompression =
800 // 1) misc keys
801 (compressQtKey >= Qt::Key_Escape && compressQtKey <= Qt::Key_SysReq)
802 // 2) cursor movement
803 || (compressQtKey >= Qt::Key_Home && compressQtKey <= Qt::Key_PageDown)
804 // 3) extra keys
805 || (compressQtKey >= Qt::Key_Super_L && compressQtKey <= Qt::Key_Direction_R)
806 // 4) something that a) doesn't translate to text or b) translates
807 // to newline text
808 || (compressQtKey == 0)
809 || (compressChar == QLatin1Char('\n'))
810 || (compressQtKey == Qt::Key_unknown);
811
812 if (compressMod == modifiers && !compressChar.isNull() && !stopCompression) {
813#ifdef DEBUG_KEY_BINDINGS
814 qDebug("compressing away %c", compressChar.toLatin1());
815#endif
816 text += compressChar;
817 // Clean up
818 RemoveEventFromQueue(GetMainEventQueue(), releaseEvent);
819 RemoveEventFromQueue(GetMainEventQueue(), pressEvent);
820 } else {
821#ifdef DEBUG_KEY_BINDINGS
822 qDebug("stoping compression..");
823#endif
824 break;
825 }
826 }
827 }
828
829 // There is no way to get the scan code from carbon. But we cannot use the value 0, since
830 // it indicates that the event originates from somewhere else than the keyboard
831 UInt32 macScanCode = 1;
832 UInt32 macVirtualKey = 0;
833 GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
834 UInt32 macModifiers = 0;
835 GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
836 sizeof(macModifiers), 0, &macModifiers);
837 handled_event = QKeyMapper::sendKeyEvent(widget, grab,
838 (ekind == kEventRawKeyUp) ? QEvent::KeyRelease : QEvent::KeyPress,
839 qtKey, modifiers, text, ekind == kEventRawKeyRepeat, 0,
840 macScanCode, macVirtualKey, macModifiers
841#ifdef QT_MAC_USE_COCOA
842 ,static_cast<bool *>(info)
843#endif
844 );
845 }
846 return handled_event;
847}
848
849void
850QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void *)
851{
852 UInt32 macVirtualKey = 0;
853 GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
854 if (updateKeyboard())
855 QKeyMapper::changeKeyboard();
856 else if (keyLayout[macVirtualKey])
857 return;
858
859 UniCharCount buffer_size = 10;
860 UniChar buffer[buffer_size];
861 keyLayout[macVirtualKey] = new KeyboardLayoutItem;
862 for (int i = 0; i < 16; ++i) {
863 UniCharCount out_buffer_size = 0;
864 keyLayout[macVirtualKey]->qtKey[i] = 0;
865#ifdef Q_WS_MAC32
866 if (keyboard_mode == UnicodeMode) {
867#endif
868 const UInt32 keyModifier = ((qt_mac_get_mac_modifiers(ModsTbl[i]) >> 8) & 0xFF);
869 OSStatus err = UCKeyTranslate(keyboard_layout_format.unicode, macVirtualKey, kUCKeyActionDown, keyModifier,
870 keyboard_kind, 0, &keyboard_dead, buffer_size, &out_buffer_size, buffer);
871 if (err == noErr && out_buffer_size) {
872 const QChar unicode(buffer[0]);
873 int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
874 if (qtkey == Qt::Key_unknown)
875 qtkey = unicode.unicode();
876 keyLayout[macVirtualKey]->qtKey[i] = qtkey;
877 }
878#ifdef Q_WS_MAC32
879 } else {
880 const UInt32 keyModifier = (qt_mac_get_mac_modifiers(ModsTbl[i]));
881
882 uchar translatedChar = KeyTranslate(keyboard_layout_format.other, keyModifier | macVirtualKey, &keyboard_dead);
883 if (translatedChar) {
884 static QTextCodec *c = 0;
885 if (!c)
886 c = QTextCodec::codecForName("Apple Roman");
887 const QChar unicode(c->toUnicode((const char *)&translatedChar, 1).at(0));
888 int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
889 if (qtkey == Qt::Key_unknown)
890 qtkey = unicode.unicode();
891 keyLayout[macVirtualKey]->qtKey[i] = qtkey;
892 }
893 }
894#endif
895 }
896#ifdef DEBUG_KEY_MAPS
897 qDebug("updateKeyMap for virtual key = 0x%02x!", (uint)macVirtualKey);
898 for (int i = 0; i < 16; ++i) {
899 qDebug(" [%d] (%d,0x%02x,'%c')", i,
900 keyLayout[macVirtualKey]->qtKey[i],
901 keyLayout[macVirtualKey]->qtKey[i],
902 keyLayout[macVirtualKey]->qtKey[i]);
903 }
904#endif
905}
906
907bool
908QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
909 QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
910 const QString &text, bool autorepeat, int count,
911 quint32 nativeScanCode, quint32 nativeVirtualKey,
912 quint32 nativeModifiers, bool *isAccepted)
913{
914 Q_UNUSED(count);
915 if (widget && widget->isEnabled()) {
916 bool key_event = true;
917#if defined(QT3_SUPPORT) && !defined(QT_NO_SHORTCUT)
918 if (type == QEvent::KeyPress && !grab
919 && QApplicationPrivate::instance()->use_compat()) {
920 QKeyEventEx accel_ev(type, code, modifiers,
921 text, autorepeat, qMax(1, int(text.length())),
922 nativeScanCode, nativeVirtualKey, nativeModifiers);
923 if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &accel_ev)) {
924#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
925 qDebug("KeyEvent: %s::%s consumed Accel: %s",
926 widget ? widget->metaObject()->className() : "none",
927 widget ? widget->objectName().toLatin1().constData() : "",
928 text.toLatin1().constData());
929#endif
930 key_event = false;
931 } else {
932 if (accel_ev.isAccepted()) {
933#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
934 qDebug("KeyEvent: %s::%s overrode Accel: %s",
935 widget ? widget->metaObject()->className() : "none",
936 widget ? widget->objectName().toLatin1().constData() : "",
937 text.toLatin1().constData());
938#endif
939 }
940 }
941 }
942#else
943Q_UNUSED(grab);
944#endif // QT3_SUPPORT && !QT_NO_SHORTCUT
945 if (key_event) {
946#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
947 qDebug("KeyEvent: Sending %s to %s::%s: %s 0x%08x%s",
948 type == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
949 widget ? widget->metaObject()->className() : "none",
950 widget ? widget->objectName().toLatin1().constData() : "",
951 text.toLatin1().constData(), int(modifiers),
952 autorepeat ? " Repeat" : "");
953#endif
954 QKeyEventEx ke(type, code, modifiers, text, autorepeat, qMax(1, text.length()),
955 nativeScanCode, nativeVirtualKey, nativeModifiers);
956 bool retMe = qt_sendSpontaneousEvent(widget,&ke);
957 if (isAccepted)
958 *isAccepted = ke.isAccepted();
959 return retMe;
960 }
961 }
962 return false;
963}
964
965QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.