diff options
Diffstat (limited to 'old/libqtuitest/qinputgenerator_x11.cpp')
-rw-r--r-- | old/libqtuitest/qinputgenerator_x11.cpp | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/old/libqtuitest/qinputgenerator_x11.cpp b/old/libqtuitest/qinputgenerator_x11.cpp new file mode 100644 index 0000000..11bb7e1 --- /dev/null +++ b/old/libqtuitest/qinputgenerator_x11.cpp @@ -0,0 +1,514 @@ +/**************************************************************************** +** +** 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 <QX11Info> +#include <X11/extensions/XTest.h> +#include <X11/keysym.h> + +#include "qtuitestnamespace.h" + +#define QINPUTGENERATOR_DEBUG() if (1); else qDebug() << "QInputGenerator:" + + +QMap<int,int> qt_key_to_keysym_make() +{ + QMap<int,int> m; + +#define QT_K(Qt,X) m.insert(Qt,X) + QT_K( '\n', XK_Return ); + QT_K( Qt::Key_Escape, XK_Escape ); + QT_K( Qt::Key_Tab, XK_Tab ); + QT_K( Qt::Key_Backtab, XK_ISO_Left_Tab ); + QT_K( Qt::Key_Backspace, XK_BackSpace ); + QT_K( Qt::Key_Return, XK_Return ); + QT_K( Qt::Key_Enter, XK_KP_Enter ); + QT_K( Qt::Key_Insert, XK_Insert ); + QT_K( Qt::Key_Delete, XK_Delete ); + QT_K( Qt::Key_Pause, XK_Pause ); + QT_K( Qt::Key_Print, XK_Print ); + QT_K( Qt::Key_SysReq, XK_Sys_Req ); + QT_K( Qt::Key_Home, XK_Home ); + QT_K( Qt::Key_End, XK_End ); + QT_K( Qt::Key_Left, XK_Left ); + QT_K( Qt::Key_Up, XK_Up ); + QT_K( Qt::Key_Right, XK_Right ); + QT_K( Qt::Key_Down, XK_Down ); + QT_K( Qt::Key_CapsLock, XK_Caps_Lock ); + QT_K( Qt::Key_NumLock, XK_Num_Lock ); + QT_K( Qt::Key_ScrollLock, XK_Scroll_Lock ); + QT_K( Qt::Key_F1, XK_F1 ); + QT_K( Qt::Key_F2, XK_F2 ); + QT_K( Qt::Key_F3, XK_F3 ); + QT_K( Qt::Key_F4, XK_F4 ); + QT_K( Qt::Key_F5, XK_F5 ); + QT_K( Qt::Key_F6, XK_F6 ); + QT_K( Qt::Key_F7, XK_F7 ); + QT_K( Qt::Key_F8, XK_F8 ); + QT_K( Qt::Key_F9, XK_F9 ); + QT_K( Qt::Key_F10, XK_F10 ); + QT_K( Qt::Key_F11, XK_F11 ); + QT_K( Qt::Key_F12, XK_F12 ); + QT_K( Qt::Key_F13, XK_F13 ); + QT_K( Qt::Key_F14, XK_F14 ); + QT_K( Qt::Key_F15, XK_F15 ); + QT_K( Qt::Key_F16, XK_F16 ); + QT_K( Qt::Key_F17, XK_F17 ); + QT_K( Qt::Key_F18, XK_F18 ); + QT_K( Qt::Key_F19, XK_F19 ); + QT_K( Qt::Key_F20, XK_F20 ); + QT_K( Qt::Key_F21, XK_F21 ); + QT_K( Qt::Key_F22, XK_F22 ); + QT_K( Qt::Key_F23, XK_F23 ); + QT_K( Qt::Key_F24, XK_F24 ); + QT_K( Qt::Key_F25, XK_F25 ); + QT_K( Qt::Key_F26, XK_F26 ); + QT_K( Qt::Key_F27, XK_F27 ); + QT_K( Qt::Key_F28, XK_F28 ); + QT_K( Qt::Key_F29, XK_F29 ); + QT_K( Qt::Key_F30, XK_F30 ); + QT_K( Qt::Key_F31, XK_F31 ); + QT_K( Qt::Key_F32, XK_F32 ); + QT_K( Qt::Key_F33, XK_F33 ); + QT_K( Qt::Key_F34, XK_F34 ); + QT_K( Qt::Key_F35, XK_F35 ); + QT_K( Qt::Key_Super_L, XK_Super_L ); + QT_K( Qt::Key_Super_R, XK_Super_R ); + QT_K( Qt::Key_Menu, XK_Menu ); + QT_K( Qt::Key_Hyper_L, XK_Hyper_L ); + QT_K( Qt::Key_Hyper_R, XK_Hyper_R ); + QT_K( Qt::Key_Help, XK_Help ); + QT_K( '/', XK_KP_Divide ); +// QT_K( '*', XK_KP_Multiply ); + QT_K( '-', XK_KP_Subtract ); + QT_K( '+', XK_KP_Add ); + QT_K( Qt::Key_Return, XK_KP_Enter ); + QT_K( Qt::Key_Kanji, XK_Kanji ); + QT_K( Qt::Key_Muhenkan, XK_Muhenkan ); + QT_K( Qt::Key_Henkan, XK_Henkan ); + QT_K( Qt::Key_Romaji, XK_Romaji ); + QT_K( Qt::Key_Hiragana, XK_Hiragana ); + QT_K( Qt::Key_Katakana, XK_Katakana ); + QT_K( Qt::Key_Hiragana_Katakana, XK_Hiragana_Katakana ); + QT_K( Qt::Key_Zenkaku, XK_Zenkaku ); + QT_K( Qt::Key_Hankaku, XK_Hankaku ); + QT_K( Qt::Key_Zenkaku_Hankaku, XK_Zenkaku_Hankaku ); + QT_K( Qt::Key_Touroku, XK_Touroku ); + QT_K( Qt::Key_Massyo, XK_Massyo ); + QT_K( Qt::Key_Kana_Lock, XK_Kana_Lock ); + QT_K( Qt::Key_Kana_Shift, XK_Kana_Shift ); + QT_K( Qt::Key_Eisu_Shift, XK_Eisu_Shift ); + QT_K( Qt::Key_Eisu_toggle,XK_Eisu_toggle ); + QT_K( Qt::Key_Hangul, XK_Hangul ); + QT_K( Qt::Key_Hangul_Start, XK_Hangul_Start ); + QT_K( Qt::Key_Hangul_End, XK_Hangul_End ); + QT_K( Qt::Key_Hangul_Hanja, XK_Hangul_Hanja ); + QT_K( Qt::Key_Hangul_Jamo, XK_Hangul_Jamo ); + QT_K( Qt::Key_Hangul_Romaja, XK_Hangul_Romaja ); + QT_K( Qt::Key_Hangul_Jeonja, XK_Hangul_Jeonja ); + QT_K( Qt::Key_Hangul_Banja, XK_Hangul_Banja ); + QT_K( Qt::Key_Hangul_PreHanja, XK_Hangul_PreHanja ); + QT_K( Qt::Key_Hangul_PostHanja,XK_Hangul_PostHanja ); + QT_K( Qt::Key_Hangul_Special, XK_Hangul_Special ); + + // Modifiers + QT_K( Qt::ShiftModifier, XK_Shift_L ); + QT_K( Qt::Key_Shift, XK_Shift_L ); + QT_K( Qt::ControlModifier,XK_Control_L ); + QT_K( Qt::Key_Control, XK_Control_L ); +// QT_K( Qt::AltModifier, XK_Alt_L ); +// QT_K( Qt::Key_Alt, XK_Alt_L ); + QT_K( Qt::MetaModifier, XK_Meta_L ); + QT_K( Qt::Key_Meta, XK_Meta_L ); + + QT_K( Qt::AltModifier, XK_ISO_Level3_Shift ); + QT_K( Qt::Key_Alt, XK_ISO_Level3_Shift ); + // FIXME support Qt::KeypadModifier + +#undef QT_K + + return m; +} + +QMap<int,uint> qt_button_to_x_button_make() +{ + QMap<int,uint> m; + + m.insert(Qt::LeftButton, 1); + m.insert(Qt::MidButton, 2); + m.insert(Qt::RightButton, 3); + + return m; +} + +QMap<int,int> qt_modifier_to_x_modmask_make() +{ + QMap<int,int> m; + + m.insert( Qt::ShiftModifier, ShiftMask ); + m.insert( Qt::ControlModifier, ControlMask ); +// m.insert( Qt::AltModifier, Mod1Mask ); + m.insert( Qt::MetaModifier, Mod4Mask ); + m.insert( Qt::AltModifier, Mod5Mask ); + + return m; +} + +struct QInputGeneratorPrivate +{ + 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 buttonmap; + + QPoint currentPos; + Qt::MouseButtons currentButtons; +}; + +QInputGeneratorPrivate::QInputGeneratorPrivate() + : keymap(qt_key_to_keysym_make()), + buttonmap(qt_button_to_x_button_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. + If we don't do this, the current modifiers stay activated for the current X server + 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 +{ + Display* dpy = QX11Info::display(); + if (!dpy) { + /* No X connection */ + return 0; + } + + Window root = 0; + Window child = 0; + int root_x = 0; + int root_y = 0; + int win_x = 0; + int win_y = 0; + uint buttons = 0; + + // Grab all of the pointer info, though all we really care about is the modifiers. + bool ok = false; + for (int i = 0; i < ScreenCount(dpy) && !ok; ++i) { + if (XQueryPointer(dpy, (Window)QX11Info::appRootWindow(i), &root, &child, &root_x, &root_y, + &win_x, &win_y, &buttons)) + ok = true; + } + + if (!ok) { + qWarning() << + "QInputGenerator: could not determine current state of keyboard modifiers. " + "Simulated key events may be incorrect."; + return 0; + } + + // Convert to Qt::KeyboardModifiers. + static const QMap<int,int> modmap = qt_modifier_to_x_modmask_make(); + + Qt::KeyboardModifiers ret(0); + for (unsigned i = 0; i < sizeof(q->AllModifiers)/sizeof(Qt::KeyboardModifier); ++i) { + Qt::KeyboardModifier thisMod = q->AllModifiers[i]; + int mask = modmap.value(thisMod); + if (buttons & mask) { + QINPUTGENERATOR_DEBUG() << "mod enabled:" << (void*)(int)thisMod << (void*)mask; + ret |= thisMod; + } + } + + QINPUTGENERATOR_DEBUG() << "current modifiers:" << (void*)buttons << (void*)(int)ret; + + return ret; +} + +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; +// } + if (currentMod != desiredMod) { + currentMod = desiredMod; + for (int i = 0; + i < 1000 && currentModifiers() != desiredMod; + i += 50, QtUiTest::wait(50)) + {} + + if (currentModifiers() != desiredMod) + qWarning() << "QtUitest: couldn't set all modifiers to desired state! " + "Current state:" << (void*)(int)currentModifiers() << + "Desired:" << (void*)(int)desiredMod; + } + +} + +void QInputGeneratorPrivate::keyEvent(Qt::Key key, bool is_press) +{ + Display* dpy = QX11Info::display(); + if (!dpy) { + /* No X connection */ + return; + } + + KeySym sym = 0; + + do { +// if ((key >= Qt::Key_0 && key <= Qt::Key_9) || +// (key >= Qt::Key_A && key <= Qt::Key_Z)) { +// sym = QChar(key).toLower().unicode(); +// break; +// } + + if (key >= Qt::Key_A && key <= Qt::Key_Z) { + sym = QChar(key).toLower().unicode(); + break; + } + + + QMap<int,int>::const_iterator found = keymap.find(key); + if (found != keymap.end()) { + sym = *found; + break; + } + + if ((key < 0x1000 && key >= 0x20 )|| (key >= Qt::Key_0 && key <= Qt::Key_9) ){ + sym = QChar(key).unicode(); + KeyCode kc = XKeysymToKeycode(dpy, sym); + int syms_per_keycode; + KeySym *keymap = XGetKeyboardMapping(dpy, kc, 1, &syms_per_keycode); + for(int i=0; i<syms_per_keycode;i++) + { + QINPUTGENERATOR_DEBUG()<<"keymap["<<i<<"]is:"<<XKeysymToString(keymap[i]); + } + + if (sym == keymap[1] && sym != keymap[0]) { + ensureModifiers(Qt::ShiftModifier); + } + + else if (sym == keymap[4]) { + ensureModifiers(Qt::AltModifier); + } + + XFree(keymap); + break; + } + + qWarning() << "QInputGenerator: don't know how to translate Qt key" + << (void*)key << "into X11 keysym"; + return; + + } while(0); + + QINPUTGENERATOR_DEBUG() << (is_press ? "press" : "release") << XKeysymToString(sym); + + XTestFakeKeyEvent( + dpy, + XKeysymToKeycode(dpy, sym), + is_press, + 0 + ); +} + +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) +{ + Display* dpy = QX11Info::display(); + if (!dpy) { + /* No X connection */ + return; + } + + if (currentPos != pos) { + currentPos = pos; + + XTestFakeMotionEvent( + dpy, + QX11Info::appScreen(), + pos.x(), + pos.y(), + 0 + ); + + 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,bool> ButtonEvent; + QList<ButtonEvent> buttonEvents; + + foreach (int button, buttonmap.keys()) { + bool desired = button & state; + bool current = button & currentButtons; + + // Do we need to press? + if (desired && !current) { + buttonEvents << qMakePair(buttonmap[button], true); + } + + // Do we need to release? + if (!desired && current) { + buttonEvents << qMakePair(buttonmap[button], false); + } + } + + foreach (ButtonEvent const& event, buttonEvents) { + QINPUTGENERATOR_DEBUG() << "Button event at" << pos << ":" << event.first << event.second; + XTestFakeButtonEvent( + dpy, + event.first, + event.second, + 0 + ); + } + + 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); +} + |