8c4c7d7baa
This is to make setting the absolute cursor position work. The remote desktop portal only allows screencasting sessions to use NotifyPointerMotionAbsolute.
527 lines
21 KiB
C++
527 lines
21 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 <KConfigGroup>
|
|
#include <KLocalizedString>
|
|
#include <KSharedConfig>
|
|
#include <QDBusPendingCallWatcher>
|
|
|
|
#include <libei.h>
|
|
#include <linux/input.h>
|
|
#include <sys/mman.h>
|
|
#include <unistd.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);
|
|
|
|
class Xkb
|
|
{
|
|
public:
|
|
Xkb()
|
|
{
|
|
m_xkbcontext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
|
|
m_xkbkeymap.reset(xkb_keymap_new_from_names(m_xkbcontext.get(), nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS));
|
|
m_xkbstate.reset(xkb_state_new(m_xkbkeymap.get()));
|
|
}
|
|
Xkb(int keymapFd, int size)
|
|
{
|
|
m_xkbcontext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
|
|
char *map = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, keymapFd, 0));
|
|
if (map != MAP_FAILED) {
|
|
m_xkbkeymap.reset(xkb_keymap_new_from_string(m_xkbcontext.get(), map, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS));
|
|
munmap(map, size);
|
|
}
|
|
close(keymapFd);
|
|
if (!m_xkbkeymap) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Failed to create keymap";
|
|
m_xkbkeymap.reset(xkb_keymap_new_from_names(m_xkbcontext.get(), nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS));
|
|
}
|
|
m_xkbstate.reset(xkb_state_new(m_xkbkeymap.get()));
|
|
}
|
|
|
|
std::optional<int> keycodeFromKeysym(xkb_keysym_t keysym)
|
|
{
|
|
auto layout = xkb_state_serialize_layout(m_xkbstate.get(), XKB_STATE_LAYOUT_EFFECTIVE);
|
|
const xkb_keycode_t max = xkb_keymap_max_keycode(m_xkbkeymap.get());
|
|
for (xkb_keycode_t keycode = xkb_keymap_min_keycode(m_xkbkeymap.get()); keycode < max; keycode++) {
|
|
uint levelCount = xkb_keymap_num_levels_for_key(m_xkbkeymap.get(), keycode, layout);
|
|
for (uint currentLevel = 0; currentLevel < levelCount; currentLevel++) {
|
|
const xkb_keysym_t *syms;
|
|
uint num_syms = xkb_keymap_key_get_syms_by_level(m_xkbkeymap.get(), keycode, layout, currentLevel, &syms);
|
|
for (uint sym = 0; sym < num_syms; sym++) {
|
|
if (syms[sym] == keysym) {
|
|
return {keycode - 8};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
private:
|
|
template<auto D>
|
|
using deleter = std::integral_constant<decltype(D), D>;
|
|
std::unique_ptr<xkb_context, deleter<xkb_context_unref>> m_xkbcontext;
|
|
std::unique_ptr<xkb_keymap, deleter<xkb_keymap_unref>> m_xkbkeymap;
|
|
std::unique_ptr<xkb_state, deleter<xkb_state_unref>> m_xkbstate;
|
|
};
|
|
|
|
RemoteDesktopSession::RemoteDesktopSession()
|
|
: iface(new OrgFreedesktopPortalRemoteDesktopInterface(QLatin1String("org.freedesktop.portal.Desktop"),
|
|
QLatin1String("/org/freedesktop/portal/desktop"),
|
|
QDBusConnection::sessionBus(),
|
|
this))
|
|
, m_eiNotifier(QSocketNotifier::Read)
|
|
, m_xkb(new Xkb)
|
|
{
|
|
connect(&m_eiNotifier, &QSocketNotifier::activated, this, &RemoteDesktopSession::handleEiEvents);
|
|
}
|
|
|
|
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());
|
|
QVariantMap startParameters = {
|
|
{QLatin1String("handle_token"), QStringLiteral("kdeconnect%1").arg(QRandomGenerator::global()->generate())},
|
|
{QStringLiteral("types"), QVariant::fromValue<uint>(7)}, // request all (KeyBoard, Pointer, TouchScreen)
|
|
{QLatin1String("persist_mode"), QVariant::fromValue<uint>(2)}, // Persist permission until explicitly revoked by user
|
|
};
|
|
|
|
KConfigGroup stateConfig = KSharedConfig::openStateConfig()->group(QStringLiteral("mousepad"));
|
|
QString restoreToken = stateConfig.readEntry(QStringLiteral("RestoreToken"), QString());
|
|
if (restoreToken.length() > 0) {
|
|
startParameters[QLatin1String("restore_token")] = restoreToken;
|
|
}
|
|
|
|
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;
|
|
return;
|
|
}
|
|
|
|
bool b = QDBusConnection::sessionBus().connect(QString(),
|
|
reply.value().path(),
|
|
QLatin1String("org.freedesktop.portal.Request"),
|
|
QLatin1String("Response"),
|
|
this,
|
|
SLOT(handleXdpSessionStarted(uint, QVariantMap)));
|
|
Q_ASSERT(b);
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "starting" << reply.value().path();
|
|
});
|
|
}
|
|
|
|
void RemoteDesktopSession::handleXdpSessionStarted(uint code, const QVariantMap &results)
|
|
{
|
|
Q_UNUSED(code);
|
|
|
|
KConfigGroup stateConfig = KSharedConfig::openStateConfig()->group(QStringLiteral("mousepad"));
|
|
stateConfig.writeEntry(QStringLiteral("RestoreToken"), results[QStringLiteral("restore_token")].toString());
|
|
auto call = iface->ConnectToEIS(m_xdpPath, {});
|
|
connect(new QDBusPendingCallWatcher(call), &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher *watcher) {
|
|
watcher->deleteLater();
|
|
QDBusReply<QDBusUnixFileDescriptor> reply = *watcher;
|
|
if (!reply.isValid()) {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Error connecting to eis" << reply.error();
|
|
return;
|
|
}
|
|
connectToEi(reply.value().takeFileDescriptor());
|
|
});
|
|
}
|
|
|
|
void RemoteDesktopSession::connectToEi(int fd)
|
|
{
|
|
m_eiNotifier.setSocket(fd);
|
|
m_eiNotifier.setEnabled(true);
|
|
m_ei = ei_new_sender(this);
|
|
ei_log_set_handler(m_ei, nullptr);
|
|
ei_setup_backend_fd(m_ei, fd);
|
|
}
|
|
|
|
void RemoteDesktopSession::handleEiEvents()
|
|
{
|
|
ei_dispatch(m_ei);
|
|
while (auto event = ei_get_event(m_ei)) {
|
|
const auto type = ei_event_get_type(event);
|
|
switch (type) {
|
|
case EI_EVENT_CONNECT:
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "Connected to ei";
|
|
break;
|
|
case EI_EVENT_DISCONNECT:
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "Disconnected from ei";
|
|
break;
|
|
case EI_EVENT_SEAT_ADDED: {
|
|
auto seat = ei_event_get_seat(event);
|
|
ei_seat_bind_capabilities(seat,
|
|
EI_DEVICE_CAP_KEYBOARD,
|
|
EI_DEVICE_CAP_POINTER,
|
|
EI_DEVICE_CAP_POINTER_ABSOLUTE,
|
|
EI_DEVICE_CAP_BUTTON,
|
|
EI_DEVICE_CAP_SCROLL,
|
|
nullptr);
|
|
break;
|
|
}
|
|
case EI_EVENT_SEAT_REMOVED:
|
|
break;
|
|
case EI_EVENT_DEVICE_ADDED: {
|
|
auto device = ei_event_get_device(event);
|
|
if (ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) && !m_keyboard) {
|
|
auto keymap = ei_device_keyboard_get_keymap(device);
|
|
if (ei_keymap_get_type(keymap) != EI_KEYMAP_TYPE_XKB) {
|
|
break;
|
|
}
|
|
m_xkb.reset(new Xkb(ei_keymap_get_fd(keymap), ei_keymap_get_size(keymap)));
|
|
m_keyboard = device;
|
|
}
|
|
if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) && !m_pointer) {
|
|
m_pointer = device;
|
|
}
|
|
if (ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) && !m_absolutePointer) {
|
|
m_absolutePointer = device;
|
|
}
|
|
} break;
|
|
case EI_EVENT_DEVICE_REMOVED: {
|
|
auto device = ei_event_get_device(event);
|
|
if (device == m_keyboard) {
|
|
m_keyboard = nullptr;
|
|
}
|
|
if (device == m_pointer) {
|
|
m_pointer = nullptr;
|
|
}
|
|
if (device == m_absolutePointer) {
|
|
m_absolutePointer = nullptr;
|
|
}
|
|
break;
|
|
}
|
|
case EI_EVENT_DEVICE_PAUSED:
|
|
break;
|
|
case EI_EVENT_DEVICE_RESUMED:
|
|
ei_device_start_emulating(ei_event_get_device(event), 0);
|
|
break;
|
|
|
|
case EI_EVENT_FRAME:
|
|
case EI_EVENT_POINTER_MOTION:
|
|
case EI_EVENT_POINTER_MOTION_ABSOLUTE:
|
|
case EI_EVENT_BUTTON_BUTTON:
|
|
case EI_EVENT_SCROLL_DELTA:
|
|
case EI_EVENT_SCROLL_DISCRETE:
|
|
case EI_EVENT_SCROLL_STOP:
|
|
case EI_EVENT_SCROLL_CANCEL:
|
|
case EI_EVENT_KEYBOARD_MODIFIERS:
|
|
case EI_EVENT_KEYBOARD_KEY:
|
|
case EI_EVENT_DEVICE_START_EMULATING:
|
|
case EI_EVENT_DEVICE_STOP_EMULATING:
|
|
case EI_EVENT_TOUCH_DOWN:
|
|
case EI_EVENT_TOUCH_MOTION:
|
|
case EI_EVENT_TOUCH_UP:
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "Unexpected event of type" << ei_event_get_type(event);
|
|
break;
|
|
}
|
|
ei_event_unref(event);
|
|
}
|
|
}
|
|
|
|
void RemoteDesktopSession::handleXdpSessionFinished(uint /*code*/, const QVariantMap & /*results*/)
|
|
{
|
|
m_xdpPath = {};
|
|
m_eiNotifier.setEnabled(false);
|
|
ei_unref(m_ei);
|
|
m_pointer = nullptr;
|
|
m_keyboard = nullptr;
|
|
m_absolutePointer = nullptr;
|
|
}
|
|
|
|
void RemoteDesktopSession::pointerButton(int button, bool down)
|
|
{
|
|
if (m_ei && m_pointer) {
|
|
ei_device_button_button(m_pointer, button, down);
|
|
ei_device_frame(m_pointer, ei_now(m_ei));
|
|
} else {
|
|
iface->NotifyPointerButton(m_xdpPath, {}, BTN_LEFT, down);
|
|
}
|
|
}
|
|
|
|
void RemoteDesktopSession::pointerAxis(double dx, double dy)
|
|
{
|
|
if (m_ei && m_pointer) {
|
|
// Qt/Kdeconnect use inverted vertical scroll direction compared to libei
|
|
ei_device_scroll_delta(m_pointer, dx, dy * -1);
|
|
ei_device_frame(m_pointer, ei_now(m_ei));
|
|
} else {
|
|
iface->NotifyPointerAxis(m_xdpPath, {}, dx, dy);
|
|
}
|
|
}
|
|
|
|
void RemoteDesktopSession::pointerMotion(double dx, double dy)
|
|
{
|
|
if (m_ei && m_pointer) {
|
|
ei_device_pointer_motion(m_pointer, dx, dy);
|
|
ei_device_frame(m_pointer, ei_now(m_ei));
|
|
} else {
|
|
iface->NotifyPointerMotion(m_xdpPath, {}, dx, dy);
|
|
}
|
|
}
|
|
|
|
void RemoteDesktopSession::pointerMotionAbsolute(double x, double y)
|
|
{
|
|
if (m_ei && m_absolutePointer) {
|
|
qDebug() << x << y;
|
|
ei_device_pointer_motion_absolute(m_absolutePointer, x, y);
|
|
ei_device_frame(m_absolutePointer, ei_now(m_ei));
|
|
} else {
|
|
iface->NotifyPointerMotionAbsolute(m_xdpPath, {}, 0, x, y);
|
|
}
|
|
}
|
|
|
|
void RemoteDesktopSession::keyboardKeycode(int key, bool press)
|
|
{
|
|
if (m_ei && m_keyboard) {
|
|
ei_device_keyboard_key(m_keyboard, key, press);
|
|
ei_device_frame(m_keyboard, ei_now(m_ei));
|
|
} else {
|
|
iface->NotifyKeyboardKeycode(m_xdpPath, {}, key, press);
|
|
}
|
|
}
|
|
|
|
void RemoteDesktopSession::keyboardKeysym(int sym, bool press)
|
|
{
|
|
if (m_ei && m_keyboard) {
|
|
if (auto code = m_xkb->keycodeFromKeysym(sym)) {
|
|
ei_device_keyboard_key(m_keyboard, *code, press);
|
|
ei_device_frame(m_keyboard, ei_now(m_ei));
|
|
} else {
|
|
qCWarning(KDECONNECT_PLUGIN_MOUSEPAD) << "failed to convert keysym" << sym;
|
|
}
|
|
} else {
|
|
iface->NotifyKeyboardKeysym(m_xdpPath, {}, sym, press).waitForFinished();
|
|
}
|
|
}
|
|
|
|
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 float x = np.get<float>(QStringLiteral("x"), 0);
|
|
const float y = np.get<float>(QStringLiteral("y"), 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->pointerButton(BTN_LEFT, true);
|
|
s_session->pointerButton(BTN_LEFT, false);
|
|
} else if (isDoubleClick) {
|
|
s_session->pointerButton(BTN_LEFT, true);
|
|
s_session->pointerButton(BTN_LEFT, false);
|
|
s_session->pointerButton(BTN_LEFT, true);
|
|
s_session->pointerButton(BTN_LEFT, false);
|
|
} else if (isMiddleClick) {
|
|
s_session->pointerButton(BTN_MIDDLE, true);
|
|
s_session->pointerButton(BTN_MIDDLE, false);
|
|
} else if (isRightClick) {
|
|
s_session->pointerButton(BTN_RIGHT, true);
|
|
s_session->pointerButton(BTN_RIGHT, false);
|
|
} else if (isSingleHold) {
|
|
// For drag'n drop
|
|
s_session->pointerButton(BTN_LEFT, true);
|
|
} else if (isSingleRelease) {
|
|
s_session->pointerButton(BTN_LEFT, false);
|
|
} else if (isScroll) {
|
|
s_session->pointerAxis(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->keyboardKeycode(KEY_LEFTCTRL, true);
|
|
if (alt)
|
|
s_session->keyboardKeycode(KEY_LEFTALT, true);
|
|
if (shift)
|
|
s_session->keyboardKeycode(KEY_LEFTSHIFT, true);
|
|
if (super)
|
|
s_session->keyboardKeycode(KEY_LEFTMETA, true);
|
|
|
|
if (specialKey) {
|
|
s_session->keyboardKeycode(SpecialKeysMap[specialKey], true);
|
|
s_session->keyboardKeycode(SpecialKeysMap[specialKey], false);
|
|
} 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->keyboardKeysym(keysym, true);
|
|
s_session->keyboardKeysym(keysym, false);
|
|
} else {
|
|
qCDebug(KDECONNECT_PLUGIN_MOUSEPAD) << "Cannot send character" << character;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ctrl)
|
|
s_session->keyboardKeycode(KEY_LEFTCTRL, false);
|
|
if (alt)
|
|
s_session->keyboardKeycode(KEY_LEFTALT, false);
|
|
if (shift)
|
|
s_session->keyboardKeycode(KEY_LEFTSHIFT, false);
|
|
if (super)
|
|
s_session->keyboardKeycode(KEY_LEFTMETA, false);
|
|
}
|
|
} else { // Is a mouse move event
|
|
if (dx || dy)
|
|
s_session->pointerMotion(dx, dy);
|
|
else if ((np.has(QStringLiteral("x")) || np.has(QStringLiteral("y"))))
|
|
s_session->pointerMotionAbsolute(x, y);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#include "moc_waylandremoteinput.cpp"
|