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 };