SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "kwin_wl_backend.h"
#include "kwin_wl_device.h"
#include <algorithm>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusReply>
#include <QKeySequence>
#include <QStringList>
#include "logging.h"
KWinWaylandBackend::KWinWaylandBackend(QObject *parent)
: InputBackend(parent)
{
m_mode = InputBackendMode::KWinWayland;
m_deviceManager = new QDBusInterface(QStringLiteral("org.kde.KWin"),
QStringLiteral("/org/kde/KWin/InputDevice"),
QStringLiteral("org.kde.KWin.InputDeviceManager"),
QDBusConnection::sessionBus(),
this);
findDevices();
m_deviceManager->connection().connect(QStringLiteral("org.kde.KWin"),
QStringLiteral("/org/kde/KWin/InputDevice"),
QStringLiteral("org.kde.KWin.InputDeviceManager"),
QStringLiteral("deviceAdded"),
this,
SLOT(onDeviceAdded(QString)));
m_deviceManager->connection().connect(QStringLiteral("org.kde.KWin"),
QStringLiteral("/org/kde/KWin/InputDevice"),
QStringLiteral("org.kde.KWin.InputDeviceManager"),
QStringLiteral("deviceRemoved"),
this,
SLOT(onDeviceRemoved(QString)));
}
KWinWaylandBackend::~KWinWaylandBackend()
{
qDeleteAll(m_devices);
delete m_deviceManager;
}
void KWinWaylandBackend::findDevices()
{
QStringList devicesSysNames;
const QVariant replyDevicesSysNames = m_deviceManager->property("devicesSysNames");
if (replyDevicesSysNames.isValid()) {
qCDebug(KCM_MOUSE) << "Devices list received successfully from KWin.";
devicesSysNames = replyDevicesSysNames.toStringList();
} else {
qCCritical(KCM_MOUSE) << "Error on receiving device list from KWin.";
m_errorString = i18n("Querying input devices failed. Please reopen this settings module.");
return;
}
for (const QString &sn : qAsConst(devicesSysNames)) {
QDBusInterface deviceIface(QStringLiteral("org.kde.KWin"),
QStringLiteral("/org/kde/KWin/InputDevice/") + sn,
QStringLiteral("org.kde.KWin.InputDevice"),
QDBusConnection::sessionBus(),
this);
QVariant reply = deviceIface.property("pointer");
if (reply.isValid() && reply.toBool()) {
reply = deviceIface.property("touchpad");
if (reply.isValid() && reply.toBool()) {
continue;
}
KWinWaylandDevice *dev = new KWinWaylandDevice(sn);
if (!dev->init()) {
qCCritical(KCM_MOUSE) << "Error on creating device object" << sn;
m_errorString = i18n("Critical error on reading fundamental device infos of %1.", sn);
return;
}
m_devices.append(dev);
qCDebug(KCM_MOUSE).nospace() << "Device found: " << dev->name() << " (" << dev->sysName() << ")";
}
}
}
bool KWinWaylandBackend::applyConfig()
{
KConfigGroup buttonGroup = KSharedConfig::openConfig("kcminputrc")->group("ButtonRebinds").group("Mouse");
for (auto it = m_buttonMapping.cbegin(); it != m_buttonMapping.cend(); ++it) {
if (auto keys = it.value().value<QKeySequence>(); !keys.isEmpty()) {
buttonGroup.writeEntry(it.key(), QStringList{"Key", keys.toString(QKeySequence::PortableText)}, KConfig::Notify);
} else {
buttonGroup.deleteEntry(it.key(), KConfig::Notify);
}
}
return std::all_of(m_devices.constBegin(), m_devices.constEnd(), [](QObject *t) {
return static_cast<KWinWaylandDevice *>(t)->applyConfig();
});
}
bool KWinWaylandBackend::getConfig()
{
m_loadedButtonMapping.clear();
const KConfigGroup buttonGroup = KSharedConfig::openConfig("kcminputrc")->group("ButtonRebinds").group("Mouse");
for (int i = 1; i <= 24; ++i) {
const QString buttonName = QLatin1String("ExtraButton%1").arg(QString::number(i));
auto entry = buttonGroup.readEntry(buttonName, QStringList());
if (entry.size() == 2 && entry.first() == QLatin1String("Key")) {
auto keys = QKeySequence::fromString(entry.at(1), QKeySequence::PortableText);
if (!keys.isEmpty()) {
m_loadedButtonMapping.insert(buttonName, keys);
}
}
}
m_buttonMapping = m_loadedButtonMapping;
return std::all_of(m_devices.constBegin(), m_devices.constEnd(), [](QObject *t) {
return static_cast<KWinWaylandDevice *>(t)->init();
});
}
bool KWinWaylandBackend::getDefaultConfig()
{
return std::all_of(m_devices.constBegin(), m_devices.constEnd(), [](QObject *t) {
return static_cast<KWinWaylandDevice *>(t)->getDefaultConfig();
});
}
bool KWinWaylandBackend::isChangedConfig() const
{
return m_buttonMapping != m_loadedButtonMapping || std::any_of(m_devices.constBegin(), m_devices.constEnd(), [](QObject *t) {
return static_cast<KWinWaylandDevice *>(t)->isChangedConfig();
});
}
QVariantMap KWinWaylandBackend::buttonMapping()
{
return m_buttonMapping;
}
void KWinWaylandBackend::setButtonMapping(const QVariantMap &mapping)
{
if (m_buttonMapping != mapping) {
m_buttonMapping = mapping;
Q_EMIT buttonMappingChanged();
}
}
void KWinWaylandBackend::onDeviceAdded(QString sysName)
{
if (std::any_of(m_devices.constBegin(), m_devices.constEnd(), [sysName](QObject *t) {
return static_cast<KWinWaylandDevice *>(t)->sysName() == sysName;
})) {
return;
}
QDBusInterface deviceIface(QStringLiteral("org.kde.KWin"),
QStringLiteral("/org/kde/KWin/InputDevice/") + sysName,
QStringLiteral("org.kde.KWin.InputDevice"),
QDBusConnection::sessionBus(),
this);
QVariant reply = deviceIface.property("pointer");
if (reply.isValid() && reply.toBool()) {
reply = deviceIface.property("touchpad");
if (reply.isValid() && reply.toBool()) {
return;
}
KWinWaylandDevice *dev = new KWinWaylandDevice(sysName);
if (!dev->init()) {
Q_EMIT deviceAdded(false);
return;
}
m_devices.append(dev);
qCDebug(KCM_MOUSE).nospace() << "Device connected: " << dev->name() << " (" << dev->sysName() << ")";
Q_EMIT deviceAdded(true);
}
}
void KWinWaylandBackend::onDeviceRemoved(QString sysName)
{
QVector<QObject *>::const_iterator it = std::find_if(m_devices.constBegin(), m_devices.constEnd(), [sysName](QObject *t) {
return static_cast<KWinWaylandDevice *>(t)->sysName() == sysName;
});
if (it == m_devices.cend()) {
return;
}
KWinWaylandDevice *dev = static_cast<KWinWaylandDevice *>(*it);
qCDebug(KCM_MOUSE).nospace() << "Device disconnected: " << dev->name() << " (" << dev->sysName() << ")";
int index = it - m_devices.cbegin();
m_devices.removeAt(index);
Q_EMIT deviceRemoved(index);
}