2e67f95017
The rationale is explained in https://planet.kde.org/friedrich-kossebau-2023-06-28-include-also-moc-files-of-headers/ In case of KDEConnect, it impressively speeds up compilation. Before it took 390 seconds on a clean build and with this change it took 330 seconds. This is due to the mocs_compilation having to include the header files and thus all their headers. Due to the lots of small plugins we have, this means that the same headers must be compiled plenty of times. When we include the moc files directly in the C++ file, they are already available.
264 lines
12 KiB
C++
264 lines
12 KiB
C++
/**
|
|
* SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
|
* SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
*/
|
|
|
|
#include "waylandremoteinput.h"
|
|
|
|
#include <QDebug>
|
|
#include <QSizeF>
|
|
|
|
#include <KLocalizedString>
|
|
#include <QDBusPendingCallWatcher>
|
|
|
|
#include <linux/input.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
namespace
|
|
{
|
|
// Translation table to keep in sync within all the implementations
|
|
int SpecialKeysMap[] = {
|
|
0, // Invalid
|
|
KEY_BACKSPACE, // 1
|
|
KEY_TAB, // 2
|
|
KEY_LINEFEED, // 3
|
|
KEY_LEFT, // 4
|
|
KEY_UP, // 5
|
|
KEY_RIGHT, // 6
|
|
KEY_DOWN, // 7
|
|
KEY_PAGEUP, // 8
|
|
KEY_PAGEDOWN, // 9
|
|
KEY_HOME, // 10
|
|
KEY_END, // 11
|
|
KEY_ENTER, // 12
|
|
KEY_DELETE, // 13
|
|
KEY_ESC, // 14
|
|
KEY_SYSRQ, // 15
|
|
KEY_SCROLLLOCK, // 16
|
|
0, // 17
|
|
0, // 18
|
|
0, // 19
|
|
0, // 20
|
|
KEY_F1, // 21
|
|
KEY_F2, // 22
|
|
KEY_F3, // 23
|
|
KEY_F4, // 24
|
|
KEY_F5, // 25
|
|
KEY_F6, // 26
|
|
KEY_F7, // 27
|
|
KEY_F8, // 28
|
|
KEY_F9, // 29
|
|
KEY_F10, // 30
|
|
KEY_F11, // 31
|
|
KEY_F12, // 32
|
|
};
|
|
}
|
|
|
|
Q_GLOBAL_STATIC(RemoteDesktopSession, s_session);
|
|
|
|
RemoteDesktopSession::RemoteDesktopSession()
|
|
: iface(new OrgFreedesktopPortalRemoteDesktopInterface(QLatin1String("org.freedesktop.portal.Desktop"),
|
|
QLatin1String("/org/freedesktop/portal/desktop"),
|
|
QDBusConnection::sessionBus(),
|
|
this))
|
|
{
|
|
}
|
|
|
|
void RemoteDesktopSession::createSession()
|
|
{
|
|
if (isValid()) {
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "pass, already created";
|
|
return;
|
|
}
|
|
|
|
m_connecting = true;
|
|
|
|
// create session
|
|
const auto handleToken = QStringLiteral("kdeconnect%1").arg(QRandomGenerator::global()->generate());
|
|
const auto sessionParameters = QVariantMap{{QLatin1String("session_handle_token"), handleToken}, {QLatin1String("handle_token"), handleToken}};
|
|
auto sessionReply = iface->CreateSession(sessionParameters);
|
|
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(sessionReply);
|
|
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, sessionReply](QDBusPendingCallWatcher *self) {
|
|
self->deleteLater();
|
|
if (sessionReply.isError()) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Could not create the remote control session" << sessionReply.error();
|
|
m_connecting = false;
|
|
return;
|
|
}
|
|
|
|
bool b = QDBusConnection::sessionBus().connect(QString(),
|
|
sessionReply.value().path(),
|
|
QLatin1String("org.freedesktop.portal.Request"),
|
|
QLatin1String("Response"),
|
|
this,
|
|
SLOT(handleXdpSessionCreated(uint, QVariantMap)));
|
|
Q_ASSERT(b);
|
|
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "authenticating" << sessionReply.value().path();
|
|
});
|
|
}
|
|
|
|
void RemoteDesktopSession::handleXdpSessionCreated(uint code, const QVariantMap &results)
|
|
{
|
|
if (code != 0) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Failed to create session with code" << code << results;
|
|
return;
|
|
}
|
|
m_connecting = false;
|
|
m_xdpPath = QDBusObjectPath(results.value(QLatin1String("session_handle")).toString());
|
|
const QVariantMap startParameters = {
|
|
{QLatin1String("handle_token"), QStringLiteral("kdeconnect%1").arg(QRandomGenerator::global()->generate())},
|
|
{QStringLiteral("types"), QVariant::fromValue<uint>(7)}, // request all (KeyBoard, Pointer, TouchScreen)
|
|
};
|
|
|
|
QDBusConnection::sessionBus().connect(QString(),
|
|
m_xdpPath.path(),
|
|
QLatin1String("org.freedesktop.portal.Session"),
|
|
QLatin1String("Closed"),
|
|
this,
|
|
SLOT(handleXdpSessionFinished(uint, QVariantMap)));
|
|
|
|
auto reply = iface->SelectDevices(m_xdpPath, startParameters);
|
|
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply);
|
|
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, reply](QDBusPendingCallWatcher *self) {
|
|
self->deleteLater();
|
|
if (reply.isError()) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Could not start the remote control session" << reply.error();
|
|
m_connecting = false;
|
|
return;
|
|
}
|
|
|
|
bool b = QDBusConnection::sessionBus().connect(QString(),
|
|
reply.value().path(),
|
|
QLatin1String("org.freedesktop.portal.Request"),
|
|
QLatin1String("Response"),
|
|
this,
|
|
SLOT(handleXdpSessionConfigured(uint, QVariantMap)));
|
|
Q_ASSERT(b);
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "configuring" << reply.value().path();
|
|
});
|
|
}
|
|
|
|
void RemoteDesktopSession::handleXdpSessionConfigured(uint code, const QVariantMap &results)
|
|
{
|
|
if (code != 0) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Failed to configure session with code" << code << results;
|
|
m_connecting = false;
|
|
return;
|
|
}
|
|
const QVariantMap startParameters = {
|
|
{QLatin1String("handle_token"), QStringLiteral("kdeconnect%1").arg(QRandomGenerator::global()->generate())},
|
|
};
|
|
auto reply = iface->Start(m_xdpPath, {}, startParameters);
|
|
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply);
|
|
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, reply](QDBusPendingCallWatcher *self) {
|
|
self->deleteLater();
|
|
if (reply.isError()) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Could not start the remote control session" << reply.error();
|
|
m_connecting = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
void RemoteDesktopSession::handleXdpSessionFinished(uint /*code*/, const QVariantMap & /*results*/)
|
|
{
|
|
m_xdpPath = {};
|
|
}
|
|
|
|
WaylandRemoteInput::WaylandRemoteInput(QObject *parent)
|
|
: AbstractRemoteInput(parent)
|
|
{
|
|
}
|
|
|
|
bool WaylandRemoteInput::handlePacket(const NetworkPacket &np)
|
|
{
|
|
if (!s_session->isValid()) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Unable to handle remote input. RemoteDesktop portal not authenticated";
|
|
s_session->createSession();
|
|
return false;
|
|
}
|
|
|
|
const float dx = np.get<float>(QStringLiteral("dx"), 0);
|
|
const float dy = np.get<float>(QStringLiteral("dy"), 0);
|
|
|
|
const bool isSingleClick = np.get<bool>(QStringLiteral("singleclick"), false);
|
|
const bool isDoubleClick = np.get<bool>(QStringLiteral("doubleclick"), false);
|
|
const bool isMiddleClick = np.get<bool>(QStringLiteral("middleclick"), false);
|
|
const bool isRightClick = np.get<bool>(QStringLiteral("rightclick"), false);
|
|
const bool isSingleHold = np.get<bool>(QStringLiteral("singlehold"), false);
|
|
const bool isSingleRelease = np.get<bool>(QStringLiteral("singlerelease"), false);
|
|
const bool isScroll = np.get<bool>(QStringLiteral("scroll"), false);
|
|
const QString key = np.get<QString>(QStringLiteral("key"), QLatin1String(""));
|
|
const int specialKey = np.get<int>(QStringLiteral("specialKey"), 0);
|
|
|
|
if (isSingleClick || isDoubleClick || isMiddleClick || isRightClick || isSingleHold || isSingleRelease || isScroll || !key.isEmpty() || specialKey) {
|
|
if (isSingleClick) {
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1);
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0);
|
|
} else if (isDoubleClick) {
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1);
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0);
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1);
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0);
|
|
} else if (isMiddleClick) {
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_MIDDLE, 1);
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_MIDDLE, 0);
|
|
} else if (isRightClick) {
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_RIGHT, 1);
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_RIGHT, 0);
|
|
} else if (isSingleHold) {
|
|
// For drag'n drop
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 1);
|
|
} else if (isSingleRelease) {
|
|
// For drag'n drop. NEVER USED (release is done by tapping, which actually triggers a isSingleClick). Kept here for future-proofness.
|
|
s_session->iface->NotifyPointerButton(s_session->m_xdpPath, {}, BTN_LEFT, 0);
|
|
} else if (isScroll) {
|
|
s_session->iface->NotifyPointerAxis(s_session->m_xdpPath, {}, dx, dy);
|
|
} else if (specialKey || !key.isEmpty()) {
|
|
bool ctrl = np.get<bool>(QStringLiteral("ctrl"), false);
|
|
bool alt = np.get<bool>(QStringLiteral("alt"), false);
|
|
bool shift = np.get<bool>(QStringLiteral("shift"), false);
|
|
bool super = np.get<bool>(QStringLiteral("super"), false);
|
|
|
|
if (ctrl)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTCTRL, 1);
|
|
if (alt)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTALT, 1);
|
|
if (shift)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTSHIFT, 1);
|
|
if (super)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTMETA, 1);
|
|
|
|
if (specialKey) {
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, SpecialKeysMap[specialKey], 1);
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, SpecialKeysMap[specialKey], 0);
|
|
} else if (!key.isEmpty()) {
|
|
for (const QChar character : key) {
|
|
const auto keysym = xkb_utf32_to_keysym(character.toLower().unicode());
|
|
if (keysym != XKB_KEY_NoSymbol) {
|
|
s_session->iface->NotifyKeyboardKeysym(s_session->m_xdpPath, {}, keysym, 1).waitForFinished();
|
|
s_session->iface->NotifyKeyboardKeysym(s_session->m_xdpPath, {}, keysym, 0).waitForFinished();
|
|
} else {
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "Cannot send character" << character;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctrl)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTCTRL, 0);
|
|
if (alt)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTALT, 0);
|
|
if (shift)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTSHIFT, 0);
|
|
if (super)
|
|
s_session->iface->NotifyKeyboardKeycode(s_session->m_xdpPath, {}, KEY_LEFTMETA, 0);
|
|
}
|
|
} else { // Is a mouse move event
|
|
s_session->iface->NotifyPointerMotion(s_session->m_xdpPath, {}, dx, dy);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#include "moc_waylandremoteinput.cpp"
|