diff options
Diffstat (limited to 'old/libqtuitest/qinputgenerator_mac.cpp')
-rw-r--r-- | old/libqtuitest/qinputgenerator_mac.cpp | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/old/libqtuitest/qinputgenerator_mac.cpp b/old/libqtuitest/qinputgenerator_mac.cpp new file mode 100644 index 0000000..a4adaab --- /dev/null +++ b/old/libqtuitest/qinputgenerator_mac.cpp @@ -0,0 +1,480 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of QtUiTest. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qinputgenerator_p.h" + +#include <QtCore> +#include <QtGui> + +#include <ApplicationServices/ApplicationServices.h> +#include <Carbon/Carbon.h> + +#include "qtuitestnamespace.h" +#include "private/qcore_mac_p.h" + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + +QMap<int,QPair<int,Qt::KeyboardModifiers> > qt_key_to_mac_keycode_make() +{ + QMap<int,QPair<int,Qt::KeyboardModifiers> > m; + + static UInt32 deadKeyState = 0L; + const UCKeyboardLayout *keyLayout = 0; + +#if defined(Q_OS_MAC32) + KeyboardLayoutRef keyLayoutRef = 0; + KLGetCurrentKeyboardLayout(&keyLayoutRef); + OSStatus err; + if (keyLayoutRef != 0) { + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData, + (reinterpret_cast<const void **>(&keyLayout))); + if (err != noErr) { + qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d", + long(err), __FILE__, __LINE__); + } + } +#else + QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource(); + Q_ASSERT(inputSource != 0); + CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource, + kTISPropertyUnicodeKeyLayoutData)); + keyLayout = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0; +#endif + + if (keyLayout) { + UniChar string[4]; + UniCharCount actualLength; + + for (UInt32 keyCode = 0; keyCode < 128; ++keyCode) { + if (noErr == UCKeyTranslate(keyLayout, keyCode, kUCKeyActionDisplay, 0, LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, &deadKeyState, 4, &actualLength, string)) + m.insert(int(string[0]), qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::NoModifier))); + if (noErr == UCKeyTranslate(keyLayout, keyCode, kUCKeyActionDisplay, ((shiftKey >> 8) & 0xff), LMGetKbdType(), + kUCKeyTranslateNoDeadKeysMask, &deadKeyState, 4, &actualLength, string)) + m.insert(int(string[0]), qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::ShiftModifier))); + } + } +#ifdef Q_OS_MAC32 + else { + const void *keyboard_layout; + KeyboardLayoutRef keyLayoutRef = 0; + KLGetCurrentKeyboardLayout(&keyLayoutRef); + err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData, + reinterpret_cast<const void **>(&keyboard_layout)); + + for (UInt32 keyCode = 0; keyCode < 128; ++keyCode) { + int translatedChar = KeyTranslate(keyboard_layout, keyCode, &deadKeyState); + if (translatedChar >= Qt::Key_Space && translatedChar <= Qt::Key_AsciiTilde) { + m.insert(translatedChar, qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::NoModifier))); + } + translatedChar = KeyTranslate(keyboard_layout, keyCode | shiftKey, &deadKeyState); + if (translatedChar >= Qt::Key_Space && translatedChar <= Qt::Key_AsciiTilde) { + m.insert(translatedChar, qMakePair(int(keyCode), Qt::KeyboardModifiers(Qt::ShiftModifier))); + } + + } + } +#endif + + m.insert( Qt::Key_Escape, qMakePair(53, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Tab, qMakePair(48, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Backspace, qMakePair(51, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Return, qMakePair(36, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Enter, qMakePair(76, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Insert, qMakePair(114, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Delete, qMakePair(117, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Print, qMakePair(105, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Space, qMakePair(49, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_PageUp, qMakePair(116, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_PageDown, qMakePair(121, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_End, qMakePair(119, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Home, qMakePair(115, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Left, qMakePair(123, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Up, qMakePair(126, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Right, qMakePair(124, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Down, qMakePair(125, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_CapsLock, qMakePair(57, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_NumLock, qMakePair(71, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F1, qMakePair(122, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F2, qMakePair(120, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F3, qMakePair(99, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F4, qMakePair(118, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F5, qMakePair(96, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F6, qMakePair(97, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F7, qMakePair(98, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F8, qMakePair(100, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F9, qMakePair(101, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F10, qMakePair(109, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F11, qMakePair(103, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F12, qMakePair(111, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F13, qMakePair(105, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F14, qMakePair(107, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_F15, qMakePair(113, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Menu, qMakePair(110, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Help, qMakePair(114, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_division, qMakePair(75, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_multiply, qMakePair(67, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Minus, qMakePair(78, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Plus, qMakePair(69, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Period, qMakePair(65, Qt::KeyboardModifiers(Qt::NoModifier)) ); + + // Modifiers + m.insert( Qt::ShiftModifier, qMakePair(56, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Shift, qMakePair(56, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::ControlModifier, qMakePair(55, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Control, qMakePair(55, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::AltModifier, qMakePair(58, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Alt, qMakePair(58, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::MetaModifier, qMakePair(59, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_Meta, qMakePair(55, Qt::KeyboardModifiers(Qt::NoModifier)) ); + m.insert( Qt::Key_CapsLock, qMakePair(57, Qt::KeyboardModifiers(Qt::NoModifier)) ); + + return m; +} + +QMap<int,uint> qt_button_to_mac_button_down_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGEventLeftMouseDown ); + m.insert( Qt::MidButton, kCGEventOtherMouseDown ); + m.insert( Qt::RightButton, kCGEventRightMouseDown ); + return m; +} + +QMap<int,uint> qt_button_to_mac_button_up_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGEventLeftMouseUp ); + m.insert( Qt::MidButton, kCGEventOtherMouseUp ); + m.insert( Qt::RightButton, kCGEventRightMouseUp ); + return m; +} + +QMap<int,uint> qt_button_to_mac_button_drag_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGEventLeftMouseDragged ); + m.insert( Qt::MidButton, kCGEventOtherMouseDragged ); + m.insert( Qt::RightButton, kCGEventRightMouseDragged ); + return m; +} + +QMap<int,uint> qt_button_to_mac_button_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, kCGMouseButtonLeft ); + m.insert( Qt::MidButton, kCGMouseButtonCenter ); + m.insert( Qt::RightButton, kCGMouseButtonRight ); + return m; +} + +class QInputGeneratorPrivate +{ +public: + QInputGeneratorPrivate(); + QInputGenerator* q; + + void keyEvent(Qt::Key, bool); + void mouseEvent(QPoint const&, Qt::MouseButtons); + + void ensureModifiers(Qt::KeyboardModifiers); + Qt::KeyboardModifiers currentModifiers() const; + + QMap<int,QPair<int,Qt::KeyboardModifiers> > const keyMap; + QMap<int,uint> const buttonMap; + QMap<int,uint> const buttonDownMap; + QMap<int,uint> const buttonUpMap; + QMap<int,uint> const buttonDragMap; + + QPoint currentPos; + Qt::MouseButtons currentButtons; +}; + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : keyMap(qt_key_to_mac_keycode_make()), + buttonMap(qt_button_to_mac_button_make()), + buttonDownMap(qt_button_to_mac_button_down_make()), + buttonUpMap(qt_button_to_mac_button_up_make()), + buttonDragMap(qt_button_to_mac_button_drag_make()), + currentPos(), + currentButtons(0) +{ + //Following line is workaround for a bug in Tiger... + CFRelease(CGEventCreate(NULL)); +} + +QInputGenerator::QInputGenerator(QObject* parent) + : QObject(parent), + d(new QInputGeneratorPrivate) +{ + d->q = this; + QINPUTGENERATOR_DEBUG() << "constructor"; +} + +QInputGenerator::~QInputGenerator() +{ + QINPUTGENERATOR_DEBUG() << "destructor"; + + /* + Restore all keyboard modifiers to off. + If we don't do this, the current modifiers stay activated + even when this application is closed. + Note that there is no guarantee this code actually gets run. + */ + d->ensureModifiers(0); + if (d->currentButtons) { + d->mouseEvent(d->currentPos, 0); + } + + d->q = 0; + delete d; + d = 0; +} + +/* + Returns the Qt keyboard modifiers which are currently pressed. +*/ +Qt::KeyboardModifiers QInputGeneratorPrivate::currentModifiers() const +{ + quint32 modifiers = GetCurrentKeyModifiers(); + + int state = 0; + state |= (modifiers & shiftKey ? Qt::ShiftModifier : Qt::NoModifier); + state |= (modifiers & controlKey ? Qt::ControlModifier : Qt::NoModifier); + state |= (modifiers & optionKey ? Qt::AltModifier : Qt::NoModifier); + state |= (modifiers & cmdKey ? Qt::MetaModifier : Qt::NoModifier); + + return Qt::KeyboardModifier(state); +} + +void QInputGeneratorPrivate::ensureModifiers(Qt::KeyboardModifiers desiredMod) +{ + Qt::KeyboardModifiers currentMod = currentModifiers(); + + // For each modifier.. + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + // If the modifier is currently disabled but we want it enabled, or vice-versa... + if ((desiredMod & thisMod) && !(currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Enabling modifier" << (void*)thisMod << "by press"; + // press to enable + keyEvent(q->modifierToKey(thisMod), true); + } else if (!(desiredMod & thisMod) && (currentMod & thisMod)) { + QINPUTGENERATOR_DEBUG() << "Disabling modifier" << (void*)thisMod << "by release"; + // release to disable + keyEvent(q->modifierToKey(thisMod), false); + } + } + + if (currentMod != desiredMod) { + currentMod = desiredMod; + for (int i = 0; + i < 1000 && QApplication::keyboardModifiers() & Qt::KeypadModifier != desiredMod & Qt::KeypadModifier; + i += 50, QtUiTest::wait(50)) + {} + + if (QApplication::keyboardModifiers() & Qt::KeypadModifier != desiredMod & Qt::KeypadModifier) + qWarning() << "QtUitest: couldn't set all modifiers to desired state! " + "Current state:" << (void*)(int)QApplication::keyboardModifiers() << + "Desired:" << (void*)(int)desiredMod; + } + +} + +void QInputGeneratorPrivate::keyEvent(Qt::Key key, bool is_press) +{ + int sym = 0; + Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(Qt::NoModifier); + do { + QMap<int,QPair<int,Qt::KeyboardModifiers> >::const_iterator found = keyMap.find(key); + if (found != keyMap.end()) { + sym = (*found).first; + if (key < Qt::Key_A || key > Qt::Key_Z) + modifiers = (*found).second; + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into Mac keycode"; + return; + + } while(0); + + if (modifiers) { + ensureModifiers(modifiers); + } + + //TODO: CGPostKeyboardEvent is deprecated in Snow Leopard, however the alternative (using CGEventPost) + // does not work as reliably (at least on Tiger/PPC). Further investigation required. + CGPostKeyboardEvent(0, (CGKeyCode)sym, is_press); + +#if 0 + CGEventRef event = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)sym, is_press); + CGEventPost(kCGHIDEventTap, event); + CFRelease(event); +#endif + + QINPUTGENERATOR_DEBUG() << (is_press ? "press" : "release") << sym; +} + +void QInputGenerator::keyPress(Qt::Key key, Qt::KeyboardModifiers mod, bool autoRepeat) +{ + Q_UNUSED(autoRepeat); + d->ensureModifiers(mod); + d->keyEvent(key, true); + QtUiTest::wait(1); +} + +void QInputGenerator::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod) +{ + d->ensureModifiers(mod); + d->keyEvent(key, false); + QtUiTest::wait(1); +} + +void QInputGenerator::keyClick(Qt::Key key, Qt::KeyboardModifiers mod) +{ + keyPress(key,mod); + QtUiTest::wait(20); + keyRelease(key,mod); +} + +void QInputGeneratorPrivate::mouseEvent(QPoint const& pos, Qt::MouseButtons state) +{ + CGPoint p; + p.x = (float)(pos.x()); + p.y = (float)(pos.y()); + + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + + //TODO: CGPostMouseEvent is deprecated in Snow Leopard, however the alternative (using CGEventPost) + // does not work as reliably (at least on Tiger/PPC). Further investigation required. + CGPostMouseEvent(p, true, 3, Qt::LeftButton & state, Qt::RightButton & state, Qt::MidButton & state); + +#if 0 + if (currentPos != pos) { + currentPos = pos; + QINPUTGENERATOR_DEBUG() << "Mouse is at: " << QCursor::pos(); + + CGEventType eventType = kCGEventMouseMoved; + CGEventRef eventRef = CGEventCreateMouseEvent(NULL, eventType, p, kCGMouseButtonLeft); + //This shouldn't be necessary, but it is... + CGEventSetType(eventRef, eventType); + CGEventPostToPSN(&psn, eventRef); + CFRelease(eventRef); + + for (int i = 0; + i < 1000 && QCursor::pos() != pos; + i += 50, QtUiTest::wait(50)) + {} + + QINPUTGENERATOR_DEBUG() << "Mouse is now at: " << QCursor::pos(); + + if (QCursor::pos() != pos) + qWarning() << "QtUitest: couldn't move cursor to desired point! " + "Current position:" << QCursor::pos() << + "Desired:" << pos; + } + + typedef QPair<uint,uint> ButtonEvent; + QList<ButtonEvent> buttonEvents; + + foreach (int button, buttonMap.keys()) { + bool desired = button & state; + bool current = button & currentButtons; + + // Do we need to release? + if (!desired && current) { + buttonEvents << qMakePair(buttonUpMap[button], buttonMap[button]); + QINPUTGENERATOR_DEBUG() << "releasing " << buttonMap[button]; + } + + // Do we need to press? + if (desired && !current) { + buttonEvents << qMakePair(buttonDownMap[button], buttonMap[button]); + QINPUTGENERATOR_DEBUG() << "pressing " << buttonMap[button]; + } + + // Do we need to drag? Is this necessary? + if (desired && current) { + buttonEvents << qMakePair(buttonDragMap[button], buttonMap[button]); + QINPUTGENERATOR_DEBUG() << "dragging " << buttonMap[button]; + } + } + + foreach (ButtonEvent const& event, buttonEvents) { + CGEventType eventType = event.first; + QINPUTGENERATOR_DEBUG() << "posting Quartz event " << eventType << ", " << event.second; + CGEventRef eventRef = CGEventCreateMouseEvent(NULL, eventType, p, event.second); + CGEventSetType(eventRef, eventType); + CGEventPostToPSN(&psn, eventRef); + CFRelease(eventRef); + } +#endif + + currentButtons = state; +} + +void QInputGenerator::mousePress(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse press" << pos << (void*)(int)state; + d->mouseEvent(pos, d->currentButtons | state); +} + +void QInputGenerator::mouseRelease(QPoint const& pos, Qt::MouseButtons state) +{ + QINPUTGENERATOR_DEBUG() << "Mouse release" << pos << (void*)(int)(d->currentButtons & ~state); + d->mouseEvent(pos, d->currentButtons & ~state); +} + +void QInputGenerator::mouseClick(QPoint const& pos, Qt::MouseButtons state) +{ + mousePress (pos,state); + QtUiTest::wait(1); + mouseRelease(pos,state); + QtUiTest::wait(1); +} + |