diff options
Diffstat (limited to 'old/libqtuitest/qinputgenerator_win.cpp')
-rw-r--r-- | old/libqtuitest/qinputgenerator_win.cpp | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/old/libqtuitest/qinputgenerator_win.cpp b/old/libqtuitest/qinputgenerator_win.cpp new file mode 100644 index 0000000..991755b --- /dev/null +++ b/old/libqtuitest/qinputgenerator_win.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** 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 <qt_windows.h> +#include <windows.h> +#ifdef __GNUC__ + #include <winable.h> +#endif + +#include "qtuitestnamespace.h" + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + +enum WindowsNativeModifiers { + ShiftLeft = 0x00000001, + ControlLeft = 0x00000002, + AltLeft = 0x00000004, + MetaLeft = 0x00000008, + ShiftRight = 0x00000010, + ControlRight = 0x00000020, + AltRight = 0x00000040, + MetaRight = 0x00000080, + CapsLock = 0x00000100, + NumLock = 0x00000200, + ScrollLock = 0x00000400, + ExtendedKey = 0x01000000, + + // Convenience mappings + ShiftAny = 0x00000011, + ControlAny = 0x00000022, + AltAny = 0x00000044, + MetaAny = 0x00000088, + LockAny = 0x00000700 +}; + +QMap<int,int> qt_key_to_win_vk_make() +{ + QMap<int,int> m; + + m.insert( Qt::Key_Escape, VK_ESCAPE ); + m.insert( Qt::Key_Tab, VK_TAB ); + m.insert( Qt::Key_Backspace, VK_BACK ); + m.insert( Qt::Key_Return, VK_RETURN ); + m.insert( Qt::Key_Enter, VK_RETURN ); + m.insert( Qt::Key_Insert, VK_INSERT ); + m.insert( Qt::Key_Delete, VK_DELETE ); + m.insert( Qt::Key_Pause, VK_PAUSE ); + m.insert( Qt::Key_Print, VK_SNAPSHOT ); + m.insert( Qt::Key_Mode_switch, VK_MODECHANGE ); + m.insert( Qt::Key_PageUp, VK_PRIOR ); + m.insert( Qt::Key_PageDown, VK_NEXT ); + m.insert( Qt::Key_End, VK_END ); + m.insert( Qt::Key_Home, VK_HOME ); + m.insert( Qt::Key_Sleep, VK_SLEEP ); + m.insert( Qt::Key_Left, VK_LEFT ); + m.insert( Qt::Key_Up, VK_UP ); + m.insert( Qt::Key_Right, VK_RIGHT ); + m.insert( Qt::Key_Down, VK_DOWN ); + m.insert( Qt::Key_Select, VK_SELECT ); + m.insert( Qt::Key_Printer, VK_PRINT ); + m.insert( Qt::Key_Execute, VK_EXECUTE ); + m.insert( Qt::Key_CapsLock, VK_CAPITAL ); + m.insert( Qt::Key_NumLock, VK_NUMLOCK ); + m.insert( Qt::Key_ScrollLock, VK_SCROLL ); + m.insert( Qt::Key_F1, VK_F1 ); + m.insert( Qt::Key_F2, VK_F2 ); + m.insert( Qt::Key_F3, VK_F3 ); + m.insert( Qt::Key_F4, VK_F4 ); + m.insert( Qt::Key_F5, VK_F5 ); + m.insert( Qt::Key_F6, VK_F6 ); + m.insert( Qt::Key_F7, VK_F7 ); + m.insert( Qt::Key_F8, VK_F8 ); + m.insert( Qt::Key_F9, VK_F9 ); + m.insert( Qt::Key_F10, VK_F10 ); + m.insert( Qt::Key_F11, VK_F11 ); + m.insert( Qt::Key_F12, VK_F12 ); + m.insert( Qt::Key_F13, VK_F13 ); + m.insert( Qt::Key_F14, VK_F14 ); + m.insert( Qt::Key_F15, VK_F15 ); + m.insert( Qt::Key_F16, VK_F16 ); + m.insert( Qt::Key_F17, VK_F17 ); + m.insert( Qt::Key_F18, VK_F18 ); + m.insert( Qt::Key_F19, VK_F19 ); + m.insert( Qt::Key_F20, VK_F20 ); + m.insert( Qt::Key_F21, VK_F21 ); + m.insert( Qt::Key_F22, VK_F22 ); + m.insert( Qt::Key_F23, VK_F23 ); + m.insert( Qt::Key_F24, VK_F24 ); + m.insert( Qt::Key_Menu, VK_APPS ); + m.insert( Qt::Key_Help, VK_HELP ); + m.insert( Qt::Key_Cancel, VK_CANCEL ); + m.insert( Qt::Key_Clear, VK_CLEAR ); + m.insert( Qt::Key_Play, VK_PLAY ); + m.insert( Qt::Key_Zoom, VK_ZOOM ); + +#if (_WIN32_WINNT >= 0x0500) +#if !defined(VK_OEM_BACKTAB) +# define VK_OEM_BACKTAB 0xF5 +#endif + m.insert( Qt::Key_Backtab, VK_OEM_BACKTAB ); + m.insert( Qt::Key_Back, VK_BROWSER_BACK ); + m.insert( Qt::Key_Forward, VK_BROWSER_FORWARD ); + m.insert( Qt::Key_Refresh, VK_BROWSER_REFRESH ); + m.insert( Qt::Key_Stop, VK_BROWSER_STOP ); + m.insert( Qt::Key_Search, VK_BROWSER_SEARCH ); + m.insert( Qt::Key_Favorites, VK_BROWSER_FAVORITES ); + m.insert( Qt::Key_HomePage, VK_BROWSER_HOME ); + m.insert( Qt::Key_VolumeMute, VK_VOLUME_MUTE ); + m.insert( Qt::Key_VolumeDown, VK_VOLUME_DOWN ); + m.insert( Qt::Key_VolumeUp, VK_VOLUME_UP ); + m.insert( Qt::Key_MediaNext, VK_MEDIA_NEXT_TRACK ); + m.insert( Qt::Key_MediaPrevious, VK_MEDIA_PREV_TRACK ); + m.insert( Qt::Key_MediaStop, VK_MEDIA_STOP ); + m.insert( Qt::Key_MediaPlay, VK_MEDIA_PLAY_PAUSE ); + m.insert( Qt::Key_LaunchMail, VK_LAUNCH_MAIL ); + m.insert( Qt::Key_LaunchMedia, VK_LAUNCH_MEDIA_SELECT ); + m.insert( Qt::Key_Launch0, VK_LAUNCH_APP1 ); + m.insert( Qt::Key_Launch1, VK_LAUNCH_APP2 ); +#endif + + + // Modifiers + m.insert( Qt::ShiftModifier, VK_SHIFT ); + m.insert( Qt::Key_Shift, VK_SHIFT ); + m.insert( Qt::ControlModifier, VK_CONTROL ); + m.insert( Qt::Key_Control, VK_CONTROL ); + m.insert( Qt::AltModifier, VK_MENU ); + m.insert( Qt::Key_Alt, VK_MENU ); + m.insert( Qt::MetaModifier, VK_LWIN ); + m.insert( Qt::Key_Meta, VK_LWIN ); + + return m; +} + +QMap<int,uint> qt_button_to_win_button_down_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, MOUSEEVENTF_LEFTDOWN ); + m.insert( Qt::MidButton, MOUSEEVENTF_MIDDLEDOWN ); + m.insert( Qt::RightButton, MOUSEEVENTF_RIGHTDOWN ); + +#if (_WIN32_WINNT >= 0x0500) + m.insert( Qt::XButton1, MOUSEEVENTF_XDOWN ); + m.insert( Qt::XButton2, MOUSEEVENTF_XDOWN ); +#endif + + return m; +} + +QMap<int,uint> qt_button_to_win_button_up_make() +{ + QMap<int,uint> m; + + m.insert( Qt::LeftButton, MOUSEEVENTF_LEFTUP ); + m.insert( Qt::MidButton, MOUSEEVENTF_MIDDLEUP ); + m.insert( Qt::RightButton, MOUSEEVENTF_RIGHTUP ); + +#if (_WIN32_WINNT >= 0x0500) + m.insert( Qt::XButton1, MOUSEEVENTF_XUP ); + m.insert( Qt::XButton2, MOUSEEVENTF_XUP ); +#endif + + return m; +} + +QMap<int,uint> qt_button_to_win_mousedata_make() +{ + QMap<int,uint> m; + +#if (_WIN32_WINNT >= 0x0500) + m.insert( Qt::XButton1, XBUTTON1 ); + m.insert( Qt::XButton2, XBUTTON2 ); +#endif + + 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,int> const keyMap; + QMap<int,uint> const buttonDownMap; + QMap<int,uint> const buttonUpMap; + QMap<int,uint> const buttonMouseDataMap; + + QPoint currentPos; + Qt::MouseButtons currentButtons; +}; + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : keyMap(qt_key_to_win_vk_make()), + buttonDownMap(qt_button_to_win_button_down_make()), + buttonUpMap(qt_button_to_win_button_up_make()), + buttonMouseDataMap(qt_button_to_win_mousedata_make()), + currentPos(), + currentButtons(0) +{} + +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. + 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 nModifiers = 0; + + if (QSysInfo::WindowsVersion < QSysInfo::WV_NT || QSysInfo::WindowsVersion & QSysInfo::WV_CE_based) { + nModifiers |= (GetKeyState(VK_SHIFT ) < 0 ? ShiftAny : 0); + nModifiers |= (GetKeyState(VK_CONTROL) < 0 ? ControlAny : 0); + nModifiers |= (GetKeyState(VK_MENU ) < 0 ? AltAny : 0); + nModifiers |= (GetKeyState(VK_LWIN ) < 0 ? MetaLeft : 0); + nModifiers |= (GetKeyState(VK_RWIN ) < 0 ? MetaRight : 0); + } else { + // Map native modifiers to some bit representation + nModifiers |= (GetKeyState(VK_LSHIFT ) & 0x80 ? ShiftLeft : 0); + nModifiers |= (GetKeyState(VK_RSHIFT ) & 0x80 ? ShiftRight : 0); + nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0); + nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0); + nModifiers |= (GetKeyState(VK_LMENU ) & 0x80 ? AltLeft : 0); + nModifiers |= (GetKeyState(VK_RMENU ) & 0x80 ? AltRight : 0); + nModifiers |= (GetKeyState(VK_LWIN ) & 0x80 ? MetaLeft : 0); + nModifiers |= (GetKeyState(VK_RWIN ) & 0x80 ? MetaRight : 0); + // Add Lock keys to the same bits + nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0); + nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0); + nModifiers |= (GetKeyState(VK_SCROLL ) & 0x01 ? ScrollLock : 0); + } + + int state = 0; + state |= (nModifiers & ShiftAny ? Qt::ShiftModifier : 0); + state |= (nModifiers & ControlAny ? Qt::ControlModifier : 0); + state |= (nModifiers & AltAny ? Qt::AltModifier : 0); + state |= (nModifiers & MetaAny ? Qt::MetaModifier : 0); + + 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() != desiredMod; + i += 50, QtUiTest::wait(50)) + {} + + if (QApplication::keyboardModifiers() != desiredMod) + 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 mod = 0; + + do { + if (key >= Qt::Key_0 && key <= Qt::Key_9 || + key >= Qt::Key_A && key <= Qt::Key_Z) { + sym = QChar(key).toUpper().toAscii(); + break; + } + + if (key >= Qt::Key_Space && key <= Qt::Key_AsciiTilde) { + sym = VkKeyScan(QChar(key).toAscii()); + if (sym & 0x0100) mod |= Qt::ShiftModifier; + if (sym & 0x0200) mod |= Qt::ControlModifier; + if (sym & 0x0400) mod |= Qt::AltModifier; + break; + } + + QMap<int,int>::const_iterator found = keyMap.find(key); + if (found != keyMap.end()) { + sym = *found; + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into Windows virtual key"; + return; + + } while(0); + + if (mod) { + ensureModifiers(mod); + } + + QINPUTGENERATOR_DEBUG() << (is_press ? "press" : "release") << sym; + + KEYBDINPUT kbi = {0}; + INPUT input = {0}; + + kbi.wVk = sym; + if (!is_press) kbi.dwFlags = KEYEVENTF_KEYUP; + + input.ki = kbi; + input.type = INPUT_KEYBOARD; + SendInput(1, &input, sizeof(input)); +} + +void QInputGenerator::keyPress(Qt::Key key, Qt::KeyboardModifiers mod, bool autoRepeat) +{ + Q_UNUSED(autoRepeat); + d->ensureModifiers(mod); + d->keyEvent(key, true); +} + +void QInputGenerator::keyRelease(Qt::Key key, Qt::KeyboardModifiers mod) +{ + d->ensureModifiers(mod); + d->keyEvent(key, false); +} + +void QInputGenerator::keyClick(Qt::Key key, Qt::KeyboardModifiers mod) +{ + keyPress(key,mod); + keyRelease(key,mod); +} + +void QInputGeneratorPrivate::mouseEvent(QPoint const& pos, Qt::MouseButtons state) +{ + if (currentPos != pos) { + currentPos = pos; + + MOUSEINPUT mi = {0}; + INPUT input = {0}; + + QRect rect = QApplication::desktop()->screenGeometry(); // FIXME: Uses default screen for now + + mi.dx = LONG(pos.x() * 65535.0 / rect.width() + 8); + mi.dy = LONG(pos.y() * 65535.0 / rect.height() + 8); + mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; + input.mi = mi; + input.type = INPUT_MOUSE; + SendInput(1, &input, sizeof(input)); + + for (int i = 0; + i < 1000 && QCursor::pos() != pos; + i += 50, QtUiTest::wait(50)) + {} + + 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, buttonDownMap.keys()) { + bool desired = button & state; + bool current = button & currentButtons; + + // Do we need to release? + if (!desired && current) { + buttonEvents << qMakePair(buttonUpMap[button], buttonMouseDataMap[button]); + } + + // Do we need to press? + if (desired && !current) { + buttonEvents << qMakePair(buttonDownMap[button], buttonMouseDataMap[button]); + } + } + + foreach (ButtonEvent const& event, buttonEvents) { + MOUSEINPUT mi = {0}; + INPUT input = {0}; + + mi.dwFlags = event.first; + mi.mouseData = event.second; + input.mi = mi; + input.type = INPUT_MOUSE; + SendInput(1, &input, sizeof(input)); + } + + 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); + mouseRelease(pos,state); +} + |