File indexing completed on 2024-04-14 04:51:46

0001 /**
0002  * SPDX-FileCopyrightText: 2018 Albert Vaca Cintora <albertvaka@gmail.com>
0003  * SPDX-FileCopyrightText: 2014 Ahmed I. Khalil <ahmedibrahimkhali@gmail.com>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #include "x11remoteinput.h"
0009 
0010 #include <QCursor>
0011 #include <QDebug>
0012 #include <private/qtx11extras_p.h>
0013 
0014 #include <X11/extensions/XTest.h>
0015 #include <X11/keysym.h>
0016 #include <fakekey/fakekey.h>
0017 
0018 enum MouseButtons { LeftMouseButton = 1, MiddleMouseButton = 2, RightMouseButton = 3, MouseWheelUp = 4, MouseWheelDown = 5 };
0019 
0020 // Translation table to keep in sync within all the implementations
0021 int SpecialKeysMap[] = {
0022     0, // Invalid
0023     XK_BackSpace, // 1
0024     XK_Tab, // 2
0025     XK_Linefeed, // 3
0026     XK_Left, // 4
0027     XK_Up, // 5
0028     XK_Right, // 6
0029     XK_Down, // 7
0030     XK_Page_Up, // 8
0031     XK_Page_Down, // 9
0032     XK_Home, // 10
0033     XK_End, // 11
0034     XK_Return, // 12
0035     XK_Delete, // 13
0036     XK_Escape, // 14
0037     XK_Sys_Req, // 15
0038     XK_Scroll_Lock, // 16
0039     0, // 17
0040     0, // 18
0041     0, // 19
0042     0, // 20
0043     XK_F1, // 21
0044     XK_F2, // 22
0045     XK_F3, // 23
0046     XK_F4, // 24
0047     XK_F5, // 25
0048     XK_F6, // 26
0049     XK_F7, // 27
0050     XK_F8, // 28
0051     XK_F9, // 29
0052     XK_F10, // 30
0053     XK_F11, // 31
0054     XK_F12, // 32
0055 };
0056 
0057 template<typename T, size_t N>
0058 size_t arraySize(T (&arr)[N])
0059 {
0060     (void)arr;
0061     return N;
0062 }
0063 
0064 X11RemoteInput::X11RemoteInput(QObject *parent)
0065     : AbstractRemoteInput(parent)
0066     , m_fakekey(nullptr)
0067 {
0068 }
0069 
0070 X11RemoteInput::~X11RemoteInput()
0071 {
0072     if (m_fakekey) {
0073         free(m_fakekey);
0074         m_fakekey = nullptr;
0075     }
0076 }
0077 
0078 bool isLeftHanded(Display *display)
0079 {
0080     unsigned char map[20];
0081     int num_buttons = XGetPointerMapping(display, map, 20);
0082     if (num_buttons == 1) {
0083         return false;
0084     } else if (num_buttons == 2) {
0085         return ((int)map[0] == 2 && (int)map[1] == 1);
0086     } else {
0087         return ((int)map[0] == 3 && (int)map[2] == 1);
0088     }
0089 }
0090 
0091 bool X11RemoteInput::handlePacket(const NetworkPacket &np)
0092 {
0093     float dx = np.get<float>(QStringLiteral("dx"), 0);
0094     float dy = np.get<float>(QStringLiteral("dy"), 0);
0095 
0096     bool isSingleClick = np.get<bool>(QStringLiteral("singleclick"), false);
0097     bool isDoubleClick = np.get<bool>(QStringLiteral("doubleclick"), false);
0098     bool isMiddleClick = np.get<bool>(QStringLiteral("middleclick"), false);
0099     bool isRightClick = np.get<bool>(QStringLiteral("rightclick"), false);
0100     bool isSingleHold = np.get<bool>(QStringLiteral("singlehold"), false);
0101     bool isSingleRelease = np.get<bool>(QStringLiteral("singlerelease"), false);
0102     bool isScroll = np.get<bool>(QStringLiteral("scroll"), false);
0103     QString key = np.get<QString>(QStringLiteral("key"), QLatin1String(""));
0104     int specialKey = np.get<int>(QStringLiteral("specialKey"), 0);
0105 
0106     if (isSingleClick || isDoubleClick || isMiddleClick || isRightClick || isSingleHold || isSingleRelease || isScroll || !key.isEmpty() || specialKey) {
0107         Display *display = QX11Info::display();
0108         if (!display) {
0109             return false;
0110         }
0111 
0112         bool leftHanded = isLeftHanded(display);
0113         int mainMouseButton = leftHanded ? RightMouseButton : LeftMouseButton;
0114         int secondaryMouseButton = leftHanded ? LeftMouseButton : RightMouseButton;
0115 
0116         if (isSingleClick) {
0117             XTestFakeButtonEvent(display, mainMouseButton, True, 0);
0118             XTestFakeButtonEvent(display, mainMouseButton, False, 0);
0119         } else if (isDoubleClick) {
0120             XTestFakeButtonEvent(display, mainMouseButton, True, 0);
0121             XTestFakeButtonEvent(display, mainMouseButton, False, 0);
0122             XTestFakeButtonEvent(display, mainMouseButton, True, 0);
0123             XTestFakeButtonEvent(display, mainMouseButton, False, 0);
0124         } else if (isMiddleClick) {
0125             XTestFakeButtonEvent(display, MiddleMouseButton, True, 0);
0126             XTestFakeButtonEvent(display, MiddleMouseButton, False, 0);
0127         } else if (isRightClick) {
0128             XTestFakeButtonEvent(display, secondaryMouseButton, True, 0);
0129             XTestFakeButtonEvent(display, secondaryMouseButton, False, 0);
0130         } else if (isSingleHold) {
0131             // For drag'n drop
0132             XTestFakeButtonEvent(display, mainMouseButton, True, 0);
0133         } else if (isSingleRelease) {
0134             // For drag'n drop. NEVER USED (release is done by tapping, which actually triggers a isSingleClick). Kept here for future-proofness.
0135             XTestFakeButtonEvent(display, mainMouseButton, False, 0);
0136         } else if (isScroll) {
0137             if (dy < 0) {
0138                 XTestFakeButtonEvent(display, MouseWheelDown, True, 0);
0139                 XTestFakeButtonEvent(display, MouseWheelDown, False, 0);
0140             } else if (dy > 0) {
0141                 XTestFakeButtonEvent(display, MouseWheelUp, True, 0);
0142                 XTestFakeButtonEvent(display, MouseWheelUp, False, 0);
0143             }
0144         } else if (!key.isEmpty() || specialKey) {
0145             bool ctrl = np.get<bool>(QStringLiteral("ctrl"), false);
0146             bool alt = np.get<bool>(QStringLiteral("alt"), false);
0147             bool shift = np.get<bool>(QStringLiteral("shift"), false);
0148             bool super = np.get<bool>(QStringLiteral("super"), false);
0149 
0150             if (ctrl)
0151                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Control_L), True, 0);
0152             if (alt)
0153                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Alt_L), True, 0);
0154             if (shift)
0155                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Shift_L), True, 0);
0156             if (super)
0157                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Super_L), True, 0);
0158 
0159             if (specialKey) {
0160                 if (specialKey >= (int)arraySize(SpecialKeysMap)) {
0161                     qWarning() << "Unsupported special key identifier";
0162                     return false;
0163                 }
0164 
0165                 int keycode = XKeysymToKeycode(display, SpecialKeysMap[specialKey]);
0166 
0167                 XTestFakeKeyEvent(display, keycode, True, 0);
0168                 XTestFakeKeyEvent(display, keycode, False, 0);
0169 
0170             } else {
0171                 if (!m_fakekey) {
0172                     m_fakekey = fakekey_init(display);
0173                     if (!m_fakekey) {
0174                         qWarning() << "Failed to initialize libfakekey";
0175                         return false;
0176                     }
0177                 }
0178 
0179                 // We use fakekey here instead of XTest (above) because it can handle utf characters instead of keycodes.
0180                 for (int i = 0; i < key.length(); i++) {
0181                     QByteArray utf8 = QString(key.at(i)).toUtf8();
0182                     fakekey_press(m_fakekey, (const uchar *)utf8.constData(), utf8.size(), 0);
0183                     fakekey_release(m_fakekey);
0184                 }
0185             }
0186 
0187             if (ctrl)
0188                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Control_L), False, 0);
0189             if (alt)
0190                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Alt_L), False, 0);
0191             if (shift)
0192                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Shift_L), False, 0);
0193             if (super)
0194                 XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Super_L), False, 0);
0195         }
0196 
0197         XFlush(display);
0198 
0199     } else { // Is a mouse move event
0200         QPoint point = QCursor::pos();
0201         QCursor::setPos(point.x() + (int)dx, point.y() + (int)dy);
0202     }
0203     return true;
0204 }
0205 
0206 bool X11RemoteInput::hasKeyboardSupport()
0207 {
0208     return true;
0209 }
0210 
0211 #include "moc_x11remoteinput.cpp"