File indexing completed on 2024-03-24 05:47:50
0001 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0002 // SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org> 0003 0004 #include <optional> 0005 0006 #include <QDebug> 0007 #include <QFile> 0008 #include <QGuiApplication> 0009 #include <QJsonArray> 0010 #include <QJsonDocument> 0011 #include <QJsonObject> 0012 0013 #include "interaction.h" 0014 0015 namespace 0016 { 0017 std::optional<wl_keyboard_key_state> typeToKeyState(QStringView type) 0018 { 0019 if (type == QLatin1String("keyDown")) { 0020 return WL_KEYBOARD_KEY_STATE_PRESSED; 0021 } 0022 if (type == QLatin1String("keyUp")) { 0023 return WL_KEYBOARD_KEY_STATE_RELEASED; 0024 } 0025 qWarning() << "unsupported keyboard action type" << type; 0026 return {}; 0027 } 0028 } // namespace 0029 0030 int main(int argc, char **argv) 0031 { 0032 QGuiApplication app(argc, argv); 0033 0034 const auto actionFilePath = qGuiApp->arguments().at(1); 0035 QFile actionFile(actionFilePath); 0036 if (!actionFile.open(QFile::ReadOnly)) { 0037 qWarning() << "failed to open action file" << actionFilePath; 0038 return 1; 0039 } 0040 0041 std::vector<BaseAction *> actions; 0042 0043 s_interface = new FakeInputInterface; 0044 0045 const auto document = QJsonDocument::fromJson(actionFile.readAll()); 0046 const auto jsonObject = document.object(); 0047 const auto jsonActions = jsonObject.value(QStringLiteral("actions")).toArray(); 0048 for (const auto &jsonActionSet : jsonActions) { 0049 if (auto inputType = jsonActionSet[QLatin1String("type")]; inputType == QLatin1String("key")) { 0050 for (const auto &jsonAction : jsonActionSet[QStringLiteral("actions")].toArray()) { 0051 const auto hash = jsonAction.toObject().toVariantHash(); 0052 const auto type = hash.value(QStringLiteral("type")).toString(); 0053 BaseAction *action = nullptr; 0054 0055 if (type == QLatin1String("pause")) { 0056 const ulong duration = hash.value(QStringLiteral("duration")).value<ulong>(); 0057 action = new PauseAction(duration); 0058 } else { 0059 const auto string = hash.value(QStringLiteral("value")).toString(); 0060 const QChar *character = string.unicode(); 0061 action = new KeyboardAction(*character, typeToKeyState(type).value_or(WL_KEYBOARD_KEY_STATE_RELEASED)); 0062 } 0063 0064 actions.emplace_back(action); 0065 } 0066 } else if (inputType == QLatin1String("pointer")) { 0067 /* 0068 https://github.com/SeleniumHQ/selenium/blob/6620bce4e8e9da1fee3ec5a5547afa7dece3f80e/py/selenium/webdriver/common/actions/pointer_input.py#L66 0069 def encode(self): 0070 return {"type": self.type, "parameters": {"pointerType": self.kind}, "id": self.name, "actions": self.actions} 0071 */ 0072 const QString id = jsonActionSet[QLatin1String("id")].toString(QStringLiteral("Default")); 0073 0074 PointerAction::PointerKind pointerTypeInt = PointerAction::PointerKind::Mouse; 0075 if (const QString pointerType = jsonActionSet[QLatin1String("parameters")].toObject().toVariantHash()[QStringLiteral("pointerType")].toString(); 0076 pointerType == QLatin1String("touch")) { 0077 pointerTypeInt = PointerAction::PointerKind::Touch; 0078 } else if (pointerType == QLatin1String("pen")) { 0079 pointerTypeInt = PointerAction::PointerKind::Pen; 0080 } 0081 0082 const auto pointerActions = jsonActionSet[QLatin1String("actions")].toArray(); 0083 for (const auto &pointerAction : pointerActions) { 0084 const auto hash = pointerAction.toObject().toVariantHash(); 0085 const auto duration = hash.value(QStringLiteral("duration")).value<ulong>(); 0086 0087 PointerAction::ActionType actionTypeInt = PointerAction::ActionType::Cancel; 0088 if (const QString actionType = hash.value(QStringLiteral("type")).toString(); actionType == QLatin1String("pointerDown")) { 0089 actionTypeInt = PointerAction::ActionType::Down; 0090 } else if (actionType == QLatin1String("pointerUp")) { 0091 actionTypeInt = PointerAction::ActionType::Up; 0092 } else if (actionType == QLatin1String("pointerMove")) { 0093 actionTypeInt = PointerAction::ActionType::Move; 0094 } else if (actionType == QLatin1String("pause")) { 0095 actions.emplace_back(new PauseAction(duration)); 0096 continue; 0097 } 0098 0099 PointerAction::Button button = PointerAction::Button::Left; 0100 if (pointerTypeInt == PointerAction::PointerKind::Mouse) { 0101 button = static_cast<PointerAction::Button>(hash.value(QStringLiteral("button")).toInt()); 0102 } 0103 0104 auto action = new PointerAction(pointerTypeInt, id, actionTypeInt, button, duration); 0105 0106 if (actionTypeInt == PointerAction::ActionType::Move) { 0107 // Positions relative to elements are ignored since at-spi2 can't report correct element positions. 0108 PointerAction::Origin originInt = PointerAction::Origin::Viewport; 0109 if (hash.value(QStringLiteral("origin")).toString() == QLatin1String("pointer")) { 0110 originInt = PointerAction::Origin::Pointer; 0111 } 0112 0113 const int x = hash.value(QStringLiteral("x")).toInt(); 0114 const int y = hash.value(QStringLiteral("y")).toInt(); 0115 action->setPosition({x, y}, originInt); 0116 } 0117 0118 actions.emplace_back(action); 0119 } 0120 continue; 0121 } else if (inputType == QLatin1String("wheel")) { 0122 const QString id = jsonActionSet[QLatin1String("id")].toString(QStringLiteral("Default")); 0123 const auto wheelActions = jsonActionSet[QLatin1String("actions")].toArray(); 0124 for (const auto &pointerAction : wheelActions) { 0125 const auto hash = pointerAction.toObject().toVariantHash(); 0126 const auto duration = hash.value(QStringLiteral("duration")).value<ulong>(); 0127 0128 if (const QString actionType = hash.value(QStringLiteral("type")).toString(); actionType == QLatin1String("pause")) { 0129 actions.emplace_back(new PauseAction(duration)); 0130 continue; 0131 } else { 0132 const auto x = hash.value(QStringLiteral("x")).toInt(); 0133 const auto y = hash.value(QStringLiteral("y")).toInt(); 0134 const auto deltaX = hash.value(QStringLiteral("deltaX")).toInt(); 0135 const auto deltaY = hash.value(QStringLiteral("deltaY")).toInt(); 0136 actions.emplace_back(new WheelAction(id, QPoint(x, y), QPoint(deltaX, deltaY), duration)); 0137 } 0138 } 0139 } else { 0140 qWarning() << "unsupported action type" << jsonActionSet; 0141 continue; 0142 } 0143 } 0144 0145 auto performActions = [actions = std::move(actions)] { 0146 for (auto action : actions) { 0147 action->perform(); 0148 } 0149 qDeleteAll(actions); 0150 QCoreApplication::quit(); 0151 }; 0152 0153 app.connect(s_interface, &FakeInputInterface::readyChanged, &app, performActions); 0154 0155 return app.exec(); 0156 }