blob: f49a91ef5bd4d9abc6bfd418edf85528b498f968 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QTextStream>
#include "mousesimulator.h"
extern "C" {
#include <xdo.h>
#include <X11/X.h>
#include <X11/extensions/XTest.h>
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
}
#include <unistd.h>
namespace {
const quint16 KTime_Wait_After_Click = 50000;
const quint16 KMultiply_Time_Wait_For_Shift_Key = 12;
const quint16 KWidth_Parts = 24;
const quint16 KHeight_Parts = 11;
const quint16 KTotal_Lang_Rows = 5;
}
MouseSimulator::MouseSimulator(QObject *parent, Window x11vkbWinId, quint16 x11vkbWidth, quint16 x11vkbHeight) :
QObject(parent),
m_xdo(xdo_new(nullptr)),
m_x11vkbWinId(x11vkbWinId),
m_x11vkbWidth(x11vkbWidth),
m_x11vkbHeight(x11vkbHeight)
{
}
/**
* @brief MouseSimulator::~MouseSimulator
*/
MouseSimulator::~MouseSimulator()
{
xdo_free(m_xdo);
}
void MouseSimulator::mouseLeftClickOnVkb(QPair<quint16, quint16> coordinates) const
{
xdo_move_mouse_relative_to_window(m_xdo, m_x11vkbWinId, 0, 0);
Display *dpy = m_xdo->xdpy;
XEvent event;
XQueryPointer(dpy, RootWindow(dpy,0), &event.xbutton.root,
&event.xbutton.window, &event.xbutton.x_root,
&event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y,
&event.xbutton.state);
XTestFakeMotionEvent (dpy, 0, event.xbutton.x + coordinates.first,
event.xbutton.y + coordinates.second, CurrentTime);
XSync(dpy, 0);
XTestFakeButtonEvent(dpy, Button1, True, CurrentTime);
XTestFakeButtonEvent(dpy, Button1, False, CurrentTime);
XFlush(dpy);
usleep(KTime_Wait_After_Click);
}
void MouseSimulator::setWidthHeight(QPair<quint16, quint16> pairWidthHeight)
{
m_x11vkbWidth = pairWidthHeight.first;
m_x11vkbHeight = pairWidthHeight.first;
}
void MouseSimulator::clickCtrlPlusSToSave(const Window winId) const
{
xdo_move_mouse_relative_to_window(m_xdo, winId, 5, 5);
Display *dpy = m_xdo->xdpy;
XTestFakeKeyEvent(dpy, XKeysymToKeycode( dpy, XK_Control_L ), true, CurrentTime);
XTestFakeKeyEvent(dpy, XKeysymToKeycode( dpy, XK_S ), true, CurrentTime);
XTestFakeKeyEvent(dpy, XKeysymToKeycode( dpy, XK_S ), false, CurrentTime);
XTestFakeKeyEvent(dpy, XKeysymToKeycode( dpy, XK_Control_L ), false, CurrentTime);
XFlush(dpy);
}
void MouseSimulator::clickLangKey(const quint16 layoutEnglish) const
{
if (layoutEnglish == 2) {
mouseLeftClickOnVkb(getPositionOfNotEngLayout(4,2));
} else {
mouseLeftClickOnVkb(getPosition(4,2));
}
}
void MouseSimulator::clickLangLine(const LanguageLines langLine) const
{
quint16 width = m_x11vkbWidth/KTotal_Lang_Rows;
quint16 height = m_x11vkbHeight/KHeight_Parts;
switch (langLine) {
case LanguageLines::FirstLine:
height = height*4;
break;
case LanguageLines::SecondLine:
height = height*5;
break;
case LanguageLines::ThirdLine:
height = height*6;
break;
case LanguageLines::FourthLine:
height = height*7;
break;
case LanguageLines::FifthLine:
height = height*8;
break;
}
mouseLeftClickOnVkb({width, height});
}
void MouseSimulator::clickHideKeyboard() const
{
mouseLeftClickOnVkb(getPosition(4,6));
}
QPair<quint16, quint16> MouseSimulator::getPosition(const quint16 rowNum, const quint16 keyNum) const
{
QPair<quint16, quint16> coordinates(0,0);
if (0 < rowNum && rowNum <= 4) {
coordinates.second = this->vkbPositionOfRow(rowNum);
switch (rowNum)
{
case 1:
case 3:
if (0 < keyNum && keyNum <= 11) {
coordinates.first = m_x11vkbWidth/KWidth_Parts*(keyNum*2);
}
break;
case 2:
if (0 < keyNum && keyNum <= 10) {
coordinates.first = m_x11vkbWidth/KWidth_Parts*(keyNum*2+1);
}
break;
case 4:
switch (keyNum) {
case 1:
coordinates.first = m_x11vkbWidth/KWidth_Parts*(keyNum*2);
break;
case 2:
coordinates.first = m_x11vkbWidth/KWidth_Parts*5;
break;
case 3:
coordinates.first = m_x11vkbWidth/2;
break;
case 4:
coordinates.first = m_x11vkbWidth/KWidth_Parts*18;
break;
case 5:
coordinates.first = m_x11vkbWidth/KWidth_Parts*20;
break;
case 6:
coordinates.first = m_x11vkbWidth/KWidth_Parts*(KWidth_Parts-2);
break;
default:
break;
}
break;
default:
break;
}
}
return coordinates;
}
QPair<quint16, quint16> MouseSimulator::getPositionOfNotEngLayout(const quint16 rowNum, const quint16 keyNum) const
{
QPair<quint16, quint16> coordinates(0, vkbPositionOfRow(rowNum));
if (rowNum == 4 && keyNum == 2) {
coordinates.first = m_x11vkbWidth/KWidth_Parts*4;
}
return coordinates;
}
void MouseSimulator::clickLetter(const QChar &letter) const
{
switch (letter.toLower().unicode())
{
case 'a':
mouseLeftClickOnVkb(this->getPosition(2,1));
break;
case 'b':
mouseLeftClickOnVkb(this->getPosition(3,6));
break;
case 'c':
mouseLeftClickOnVkb(this->getPosition(3,4));
break;
case 'd':
mouseLeftClickOnVkb(this->getPosition(2,3));
break;
case 'e':
mouseLeftClickOnVkb(this->getPosition(1,3));
break;
case 'f':
mouseLeftClickOnVkb(this->getPosition(2,4));
break;
case 'g':
mouseLeftClickOnVkb(this->getPosition(2,5));
break;
case 'h':
mouseLeftClickOnVkb(this->getPosition(2,6));
break;
case 'i':
mouseLeftClickOnVkb(this->getPosition(1,8));
break;
case 'j':
mouseLeftClickOnVkb(this->getPosition(2,7));
break;
case 'k':
mouseLeftClickOnVkb(this->getPosition(2,8));
break;
case 'l':
mouseLeftClickOnVkb(this->getPosition(2,9));
break;
case 'm':
mouseLeftClickOnVkb(this->getPosition(3,8));
break;
case 'n':
mouseLeftClickOnVkb(this->getPosition(3,7));
break;
case 'o':
mouseLeftClickOnVkb(this->getPosition(1,9));
break;
case 'p':
mouseLeftClickOnVkb(this->getPosition(1,10));
break;
case 'q':
mouseLeftClickOnVkb(this->getPosition(1,1));
break;
case 'r':
mouseLeftClickOnVkb(this->getPosition(1,4));
break;
case 's':
mouseLeftClickOnVkb(this->getPosition(2,2));
break;
case 't':
mouseLeftClickOnVkb(this->getPosition(1,5));
break;
case 'u':
mouseLeftClickOnVkb(this->getPosition(1,7));
break;
case 'v':
mouseLeftClickOnVkb(this->getPosition(3,5));
break;
case 'w':
mouseLeftClickOnVkb(this->getPosition(1,2));
break;
case 'x':
mouseLeftClickOnVkb(this->getPosition(3,3));
break;
case 'y':
mouseLeftClickOnVkb(this->getPosition(1,6));
break;
case 'z':
mouseLeftClickOnVkb(this->getPosition(3,2));
break;
default:
break;
}
}
void MouseSimulator::clickEnglishLetter(QChar &letter) const
{
this->clickExtraKeyIfNeeded(letter);
if (letter.isLetter()) {
this->clickLetter(letter);
} else if (letter.isNumber()) {
quint16 number = static_cast<quint16>(letter.digitValue());
mouseLeftClickOnVkb(this->getPosition(1, number == 0 ? 10 : static_cast<quint16>(number) ));
} else {
switch (letter.unicode())
{
case ' ':
mouseLeftClickOnVkb(this->getPosition(4,3));
break;
case ',':
mouseLeftClickOnVkb(this->getPosition(3,9));
break;
case '.':
mouseLeftClickOnVkb(this->getPosition(3,10));
break;
case '\'':
mouseLeftClickOnVkb(this->getPosition(4,4));
break;
case '\b':
mouseLeftClickOnVkb(this->getPosition(1,11));
break;
case QChar::CarriageReturn :
mouseLeftClickOnVkb(this->getPosition(2,10));
break;
default:
break;
}
}
this->clickExtraKeyIfNeeded(letter);
}
void MouseSimulator::clickExtraKeyIfNeeded(QChar &letter) const
{
bool clickIsNeeded = false;
if (letter.isUpper()) {
mouseLeftClickOnVkb(this->getPosition(3,1));
clickIsNeeded = true;
} else if (letter.isNumber()) {
mouseLeftClickOnVkb(this->getPosition(4,1));
clickIsNeeded = true;
}
if (clickIsNeeded) {
usleep(KTime_Wait_After_Click*KMultiply_Time_Wait_For_Shift_Key);
}
}
quint16 MouseSimulator::vkbPositionOfRow(const quint16 row) const
{
quint16 heightSize = m_x11vkbHeight/KHeight_Parts;
switch (row)
{
case 1 :
heightSize = heightSize*3;
break;
case 2 :
heightSize = heightSize*5;
break;
case 3 :
heightSize = heightSize*7;
break;
case 4 :
heightSize = heightSize*9;
break;
default:
heightSize = 0;
break;
}
return heightSize;
}