File indexing completed on 2024-04-21 04:56:50
0001 /** 0002 * SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> 0003 * SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "waylandremoteinput.h" 0009 0010 #include <QDebug> 0011 #include <QSizeF> 0012 0013 #include <KConfigGroup> 0014 #include <KLocalizedString> 0015 #include <KSharedConfig> 0016 #include <QDBusPendingCallWatcher> 0017 0018 #include <linux/input.h> 0019 #include <xkbcommon/xkbcommon.h> 0020 0021 namespace 0022 { 0023 // Translation table to keep in sync within all the implementations 0024 int SpecialKeysMap[] = { 0025 0, // Invalid 0026 KEY_BACKSPACE, // 1 0027 KEY_TAB, // 2 0028 KEY_LINEFEED, // 3 0029 KEY_LEFT, // 4 0030 KEY_UP, // 5 0031 KEY_RIGHT, // 6 0032 KEY_DOWN, // 7 0033 KEY_PAGEUP, // 8 0034 KEY_PAGEDOWN, // 9 0035 KEY_HOME, // 10 0036 KEY_END, // 11 0037 KEY_ENTER, // 12 0038 KEY_DELETE, // 13 0039 KEY_ESC, // 14 0040 KEY_SYSRQ, // 15 0041 KEY_SCROLLLOCK, // 16 0042 0, // 17 0043 0, // 18 0044 0, // 19 0045 0, // 20 0046 KEY_F1, // 21 0047 KEY_F2, // 22 0048 KEY_F3, // 23 0049 KEY_F4, // 24 0050 KEY_F5, // 25 0051 KEY_F6, // 26 0052 KEY_F7, // 27 0053 KEY_F8, // 28 0054 KEY_F9, // 29 0055 KEY_F10, // 30 0056 KEY_F11, // 31 0057 KEY_F12, // 32 0058 }; 0059 } 0060 0061 Q_GLOBAL_STATIC(RemoteDesktopSession, s_session); 0062 0063 RemoteDesktopSession::RemoteDesktopSession() 0064 : iface(new OrgFreedesktopPortalRemoteDesktopInterface(QLatin1String("org.freedesktop.portal.Desktop"), 0065 QLatin1String("/org/freedesktop/portal/desktop"), 0066 QDBusConnection::sessionBus(), 0067 this)) 0068 { 0069 } 0070 0071 void RemoteDesktopSession::createSession() 0072 { 0073 if (isValid()) { 0074 qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "pass, already created"; 0075 return; 0076 } 0077 0078 m_connecting = true; 0079 0080 // create session 0081 const auto handleToken = QStringLiteral("kdeconnect%1").arg(QRandomGenerator::global()->generate()); 0082 const auto sessionParameters = QVariantMap{{QLatin1String("session_handle_token"), handleToken}, {QLatin1String("handle_token"), handleToken}}; 0083 auto sessionReply = iface->CreateSession(sessionParameters); 0084 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(sessionReply); 0085 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, sessionReply](QDBusPendingCallWatcher *self) { 0086 self->deleteLater(); 0087 if (sessionReply.isError()) { 0088 qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Could not create the remote control session" << sessionReply.error(); 0089 m_connecting = false; 0090 return; 0091 } 0092 0093 bool b = QDBusConnection::sessionBus().connect(QString(), 0094 sessionReply.value().path(), 0095 QLatin1String("org.freedesktop.portal.Request"), 0096 QLatin1String("Response"), 0097 this, 0098 SLOT(handleXdpSessionCreated(uint, QVariantMap))); 0099 Q_ASSERT(b); 0100 0101 qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "authenticating" << sessionReply.value().path(); 0102 }); 0103 } 0104 0105 void RemoteDesktopSession::handleXdpSessionCreated(uint code, const QVariantMap &results) 0106 { 0107 if (code != 0) { 0108 qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Failed to create session with code" << code << results; 0109 return; 0110 } 0111 0112 m_connecting = false; 0113 m_xdpPath = QDBusObjectPath(results.value(QLatin1String("session_handle")).toString()); 0114 QVariantMap startParameters = { 0115 {QLatin1String("handle_token"), QStringLiteral("kdeconnect%1").arg(QRandomGenerator::global()->generate())}, 0116 {QStringLiteral("types"), QVariant::fromValue<uint>(7)}, // request all (KeyBoard, Pointer, TouchScreen) 0117 {QLatin1String("persist_mode"), QVariant::fromValue<uint>(2)}, // Persist permission until explicitly revoked by user 0118 }; 0119 0120 KConfigGroup stateConfig = KSharedConfig::openStateConfig()->group(QStringLiteral("mousepad")); 0121 QString restoreToken = stateConfig.readEntry(QStringLiteral("RestoreToken"), QString()); 0122 if (restoreToken.length() > 0) { 0123 startParameters[QLatin1String("restore_token")] = restoreToken; 0124 } 0125 0126 QDBusConnection::sessionBus().connect(QString(), 0127 m_xdpPath.path(), 0128 QLatin1String("org.freedesktop.portal.Session"), 0129 QLatin1String("Closed"), 0130 this, 0131 SLOT(handleXdpSessionFinished(uint, QVariantMap))); 0132 0133 auto reply = iface->SelectDevices(m_xdpPath, startParameters); 0134 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply); 0135 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, reply](QDBusPendingCallWatcher *self) { 0136 self->deleteLater(); 0137 if (reply.isError()) { 0138 qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Could not start the remote control session" << reply.error(); 0139 m_connecting = false; 0140 return; 0141 } 0142 0143 bool b = QDBusConnection::sessionBus().connect(QString(), 0144 reply.value().path(), 0145 QLatin1String("org.freedesktop.portal.Request"), 0146 QLatin1String("Response"), 0147 this, 0148 SLOT(handleXdpSessionConfigured(uint, QVariantMap))); 0149 Q_ASSERT(b); 0150 qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "configuring" << reply.value().path(); 0151 }); 0152 } 0153 0154 void RemoteDesktopSession::handleXdpSessionConfigured(uint code, const QVariantMap &results) 0155 { 0156 if (code != 0) { 0157 qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Failed to configure session with code" << code << results; 0158 m_connecting = false; 0159 return; 0160 } 0161 const QVariantMap startParameters = { 0162 {QLatin1String("handle_token"), QStringLiteral("kdeconnect%1").arg(QRandomGenerator::global()->generate())}, 0163 }; 0164 auto reply = iface->Start(m_xdpPath, {}, startParameters); 0165 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply); 0166 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, reply](QDBusPendingCallWatcher *self) { 0167 self->deleteLater(); 0168 if (reply.isError()) { 0169 qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Could not start the remote control session" << reply.error(); 0170 m_connecting = false; 0171 return; 0172 } 0173 0174 bool b = QDBusConnection::sessionBus().connect(QString(), 0175 reply.value().path(), 0176 QLatin1String("org.freedesktop.portal.Request"), 0177 QLatin1String("Response"), 0178 this, 0179 SLOT(handleXdpSessionStarted(uint, QVariantMap))); 0180 Q_ASSERT(b); 0181 qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "starting" << reply.value().path(); 0182 }); 0183 } 0184 0185 void RemoteDesktopSession::handleXdpSessionStarted(uint code, const QVariantMap &results) 0186 { 0187 Q_UNUSED(code); 0188 0189 KConfigGroup stateConfig = KSharedConfig::openStateConfig()->group(QStringLiteral("mousepad")); 0190 stateConfig.writeEntry(QStringLiteral("RestoreToken"), results[QStringLiteral("restore_token")].toString()); 0191 } 0192 0193 void RemoteDesktopSession::handleXdpSessionFinished(uint /*code*/, const QVariantMap & /*results*/) 0194 { 0195 m_xdpPath = {}; 0196 } 0197 0198 WaylandRemoteInput::WaylandRemoteInput(QObject *parent) 0199 : AbstractRemoteInput(parent) 0200 { 0201 } 0202 0203 bool WaylandRemoteInput::handlePacket(const NetworkPacket &np) 0204 { 0205 if (!s_session->isValid()) { 0206 qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Unable to handle remote input. RemoteDesktop portal not authenticated"; 0207 s_session->createSession(); 0208 return false; 0209 } 0210 0211 const float dx = np.get<float>(QStringLiteral("dx"), 0); 0212 const float dy = np.get<float>(QStringLiteral("dy"), 0); 0213 0214 const bool isSingleClick = np.get<bool>(QStringLiteral("singleclick"), false); 0215 const bool isDoubleClick = np.get<bool>(QStringLiteral("doubleclick"), false); 0216 const bool isMiddleClick = np.get<bool>(QStringLiteral("middleclick"), false); 0217 const bool isRightClick = np.get<bool>(QStringLiteral("rightclick"), false); 0218 const bool isSingleHold = np.get<bool>(QStringLiteral("singlehold"), false); 0219 const bool isSingleRelease = np.get<bool>(QStringLiteral("singlerelease"), false); 0220 const bool isScroll = np.get<bool>(QStringLiteral("scroll"), false); 0221 const QString key = np.get<QString>(QStringLiteral("key"), QLatin1String("")); 0222 const int specialKey = np.get<int>(QStringLiteral("specialKey"), 0); 0223 0224 if (isSingleClick || isDoubleClick || isMiddleClick || isRightClick || isSingleHold || isSingleRelease || isScroll || !key.isEmpty() || specialKey) { 0225 if (isSingleClick) { 0226 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1); 0227 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0); 0228 } else if (isDoubleClick) { 0229 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1); 0230 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0); 0231 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1); 0232 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0); 0233 } else if (isMiddleClick) { 0234 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_MIDDLE, 1); 0235 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_MIDDLE, 0); 0236 } else if (isRightClick) { 0237 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_RIGHT, 1); 0238 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_RIGHT, 0); 0239 } else if (isSingleHold) { 0240 // For drag'n drop 0241 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1); 0242 } else if (isSingleRelease) { 0243 // For drag'n drop. NEVER USED (release is done by tapping, which actually triggers a isSingleClick). Kept here for future-proofness. 0244 s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0); 0245 } else if (isScroll) { 0246 s_session->iface->NotifyPointerAxis(s_session->m_xdpPath, {}, dx, dy); 0247 } else if (specialKey || !key.isEmpty()) { 0248 bool ctrl = np.get<bool>(QStringLiteral("ctrl"), false); 0249 bool alt = np.get<bool>(QStringLiteral("alt"), false); 0250 bool shift = np.get<bool>(QStringLiteral("shift"), false); 0251 bool super = np.get<bool>(QStringLiteral("super"), false); 0252 0253 if (ctrl) 0254 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTCTRL, 1); 0255 if (alt) 0256 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTALT, 1); 0257 if (shift) 0258 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTSHIFT, 1); 0259 if (super) 0260 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTMETA, 1); 0261 0262 if (specialKey) { 0263 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, SpecialKeysMap[specialKey], 1); 0264 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, SpecialKeysMap[specialKey], 0); 0265 } else if (!key.isEmpty()) { 0266 for (const QChar character : key) { 0267 const auto keysym = xkb_utf32_to_keysym(character.toLower().unicode()); 0268 if (keysym != XKB_KEY_NoSymbol) { 0269 s_session->iface->NotifyKeyboardKeysym(s_session->m_xdpPath, {}, keysym, 1).waitForFinished(); 0270 s_session->iface->NotifyKeyboardKeysym(s_session->m_xdpPath, {}, keysym, 0).waitForFinished(); 0271 } else { 0272 qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "Cannot send character" << character; 0273 } 0274 } 0275 } 0276 0277 if (ctrl) 0278 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTCTRL, 0); 0279 if (alt) 0280 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTALT, 0); 0281 if (shift) 0282 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTSHIFT, 0); 0283 if (super) 0284 s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTMETA, 0); 0285 } 0286 } else { // Is a mouse move event 0287 s_session->iface->NotifyPointerMotion(s_session->m_xdpPath, {}, dx, dy); 0288 } 0289 return true; 0290 } 0291 0292 #include "moc_waylandremoteinput.cpp"