File indexing completed on 2024-04-21 05:44:59
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0003 SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org> 0004 SPDX-FileCopyrightText: 2024 Fushan Wen <qydwhotmail@gmail.com> 0005 */ 0006 0007 #pragma once 0008 0009 #include <memory> 0010 0011 #include "qwayland-fake-input.h" 0012 #include <QHash> 0013 #include <QMap> 0014 #include <QPoint> 0015 #include <QWaylandClientExtensionTemplate> 0016 #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) 0017 #include <qpa/qplatformnativeinterface.h> 0018 #endif 0019 0020 #include <wayland-client-protocol.h> 0021 #include <xkbcommon/xkbcommon.h> 0022 0023 namespace std 0024 { 0025 template<> 0026 struct default_delete<xkb_context> { 0027 void operator()(xkb_context *ptr) const 0028 { 0029 xkb_context_unref(ptr); 0030 } 0031 }; 0032 0033 template<> 0034 struct default_delete<xkb_keymap> { 0035 void operator()(xkb_keymap *ptr) const 0036 { 0037 xkb_keymap_unref(ptr); 0038 } 0039 }; 0040 0041 template<> 0042 struct default_delete<xkb_state> { 0043 void operator()(xkb_state *ptr) const 0044 { 0045 xkb_state_unref(ptr); 0046 } 0047 }; 0048 } // namespace std 0049 0050 class FakeInputInterface : public QWaylandClientExtensionTemplate<FakeInputInterface>, public QtWayland::org_kde_kwin_fake_input 0051 { 0052 Q_OBJECT 0053 0054 public: 0055 explicit FakeInputInterface(); 0056 ~FakeInputInterface() override; 0057 0058 void roundtrip(bool touch = false); 0059 void sendKey(const std::vector<quint32> &linuxModifiers, quint32 linuxKeyCode, wl_keyboard_key_state keyState); 0060 0061 Q_DISABLE_COPY_MOVE(FakeInputInterface) 0062 0063 Q_SIGNALS: 0064 void readyChanged(); 0065 0066 private: 0067 bool m_ready = false; 0068 wl_display *m_display = nullptr; 0069 }; 0070 0071 extern FakeInputInterface *s_interface; 0072 0073 class BaseAction 0074 { 0075 public: 0076 explicit BaseAction(); 0077 virtual ~BaseAction(); 0078 0079 virtual void perform() = 0; 0080 0081 Q_DISABLE_COPY_MOVE(BaseAction) 0082 }; 0083 0084 class KeyboardAction : public BaseAction 0085 { 0086 public: 0087 /** 0088 * @brief Construct a new Keyboard Action object 0089 * 0090 * @param key the QChar to construct the action for 0091 * 0092 * The way this works is a bit complicated. Because we tell KWin which keys to press based on linux key codes, 0093 * we effectively have to resolve the actual keys that needs pressing to generate the character on a given layout. 0094 * When running a nested KWin that is always the us layout because we set KWIN_XKB_DEFAULT_KEYMAP (which forces 0095 * KWin to follow environment-defined XKB variables). 0096 * When not running nested, things get even more complicated because KWin follows the user's layout which may 0097 * be anything. 0098 * 0099 * So we end up resolving keycodes through XKB... 0100 * XKB resolution entails iterating all levels in all keycodes to look at all keysyms and eventually find the one 0101 * we are looking for. It's a bit verbose but it is what it is. 0102 */ 0103 explicit KeyboardAction(const QChar &key, wl_keyboard_key_state keyState); 0104 ~KeyboardAction() override; 0105 0106 void perform() override; 0107 0108 std::vector<quint32> linuxModifiers() const; 0109 0110 private: 0111 // The default layout used by KWin. This is either environment-defined (for nested KWins) or read from its DBus API 0112 // when dealing with a native KWin. 0113 QByteArray defaultLayout() const; 0114 0115 void loadModifiers(); 0116 0117 // Resolve the modifiers required to produce a certain key. 0118 // XKB API is again a bit awkward here because it spits out modifier string names rather than codes or syms 0119 // so this function implicitly relies on loadModifiers() having first resolved modifiers to their stringy 0120 // representation. 0121 // Besides that it is straight forward. We request a modifier mask, check which modifiers are active in the mask 0122 // and based on that we can identifier the keycodes we need to press. 0123 void resolveModifiersForKey(xkb_keycode_t keycode); 0124 0125 xkb_keysym_t charToKeysym(const QChar &key); 0126 0127 xkb_keysym_t m_keysym = XKB_KEY_NoSymbol; 0128 xkb_keycode_t m_keycode = XKB_KEYCODE_INVALID; 0129 xkb_level_index_t m_level = XKB_LEVEL_INVALID; 0130 QStringList m_modifiers; 0131 0132 std::unique_ptr<xkb_context> m_context; 0133 xkb_rule_names m_ruleNames; 0134 std::unique_ptr<xkb_keymap> m_keymap; 0135 std::unique_ptr<xkb_state> m_state; 0136 xkb_layout_index_t m_layout; 0137 0138 xkb_mod_index_t m_modCount; 0139 QMap<uint, QList<xkb_keycode_t>> m_modifierSymToCodes; 0140 QMap<QString, uint> m_modifierNameToSym; 0141 0142 wl_keyboard_key_state m_keyState; 0143 }; 0144 0145 class PauseAction : public BaseAction 0146 { 0147 public: 0148 explicit PauseAction(unsigned long duration); 0149 ~PauseAction() override; 0150 0151 void perform() override; 0152 0153 private: 0154 unsigned long m_duration = 0; 0155 }; 0156 0157 class WheelAction : public BaseAction 0158 { 0159 public: 0160 explicit WheelAction(const QString &id, const QPoint &pos, const QPoint &deltaPos, unsigned long duration); 0161 ~WheelAction() override; 0162 0163 void perform() override; 0164 0165 private: 0166 unsigned m_uniqueId; 0167 QPoint m_pos; 0168 QPoint m_deltaPos; 0169 unsigned long m_duration = 0; 0170 }; 0171 0172 class PointerAction : public BaseAction 0173 { 0174 public: 0175 // https://github.com/SeleniumHQ/selenium/blob/6620bce4e8e9da1fee3ec5a5547afa7dece3f80e/py/selenium/webdriver/common/actions/interaction.py#L30 0176 enum class PointerKind { 0177 Mouse, 0178 Touch, 0179 Pen, 0180 }; 0181 0182 enum class ActionType { 0183 Move, 0184 Down, 0185 Up, 0186 Cancel, 0187 }; 0188 0189 enum class Button { 0190 Left = 0, 0191 Touch = 0, 0192 PenContact = 0, 0193 Middle = 1, 0194 Right = 2, 0195 PenBarrel = 2, 0196 X1 = 3, 0197 Back = 3, 0198 X2 = 4, 0199 Forward = 4, 0200 }; 0201 0202 enum class Origin { 0203 Viewport, 0204 Pointer, 0205 }; 0206 0207 explicit PointerAction(PointerKind pointerType, const QString &id, ActionType actionType, Button button, unsigned long duration); 0208 ~PointerAction() override; 0209 0210 void setPosition(const QPoint &pos, Origin origin); 0211 void perform() override; 0212 0213 private: 0214 static QHash<unsigned /* unique id */, QPoint> s_positions; 0215 static QSet<unsigned /*unique id*/> s_touchPoints; 0216 static QSet<int /* pressed button */> s_mouseButtons; 0217 0218 unsigned m_uniqueId; 0219 PointerKind m_pointerType = PointerKind::Touch; 0220 ActionType m_actionType = ActionType::Move; 0221 Button m_button = Button::Left; 0222 QPoint m_pos; 0223 Origin m_origin = Origin::Viewport; 0224 unsigned long m_duration = 0; 0225 0226 friend class WheelAction; 0227 };