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"