Commit 4f06b7cc authored by Roman Gilg's avatar Roman Gilg
Browse files

[Mouse KCM] Add KWin Wayland backend

Summary:
The primal goal of this patch is the addition of a new backend in order to
configure pointer devices through KWin's libinput backend in a Wayland session.

The implementation builds upon Xuetian's backend splitting work, which again
was based on the backend splitting in the Touchpad KCM.

To integrate this backend nicely the code structure is cleaned up and Ui code
as well as backend specific code is pushed into the respective subdirectories
similar to the Touchpad KCM code.

{F5757933}

Test Plan: Tested the old backend on X and the new libinput backend on Wayland.

Reviewers: #plasma, davidedmundson

Reviewed By: #plasma, davidedmundson

Subscribers: davidedmundson, ngraham, plasma-devel

Tags: #plasma

Maniphest Tasks: T7964

Differential Revision: https://phabricator.kde.org/D11468
parent 0b5c033b
Mouse & Keyboard Configuration Modules:
Pat Dowler (dowler@pt1B1106.FSH.UVic.CA)
Conversion to kcontrol applet:
Matthias Hoelzer (hoelzer@physik.uni-wuerzburg.de)
......@@ -5,47 +5,85 @@ endif()
# KI18N Translation Domain for this library
add_definitions(-DTRANSLATION_DOMAIN=\"kcminput\")
add_subdirectory( pics )
add_subdirectory( misc )
## Add common files here.
set(kcminput_backend_SRCS
mousebackend.cpp
mousesettings.cpp
logging.cpp)
set(kcminput_backend_LIBS)
set(common_SRCS
inputbackend.cpp
)
include(ECMQtDeclareLoggingCategory)
ecm_qt_declare_logging_category(common_SRCS
HEADER
logging.h
IDENTIFIER
KCM_INPUT
CATEGORY_NAME
kcm_input
DEFAULT_SEVERITY
Critical
)
include(backends/x11.cmake)
include(backends/kwin_wl.cmake)
########### next target ###############
add_executable(kapplymousetheme
kapplymousetheme.cpp
${common_SRCS}
${backend_SRCS}
)
target_link_libraries(kapplymousetheme
${backend_LIBS}
Qt5::Gui
Qt5::DBus
KF5::CoreAddons
KF5::ConfigCore
KF5::I18n
)
install(TARGETS kapplymousetheme ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
########### next target ###############
set(kcm_input_PART_SRCS
mouse.cpp
main.cpp
${kcminput_backend_SRCS}
set(common_SRCS
${common_SRCS}
plugin.cpp
kcm/configcontainer.cpp
kcm/configplugin.cpp
kcm/libinput/libinput_config.cpp
kcm/xlib/xlib_config.cpp
)
set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml)
ki18n_wrap_ui(kcm_input_PART_SRCS kcmmouse.ui)
qt5_add_dbus_interface(kcm_input_PART_SRCS ${klauncher_xml} klauncher_iface)
ki18n_wrap_ui(common_SRCS kcm/xlib/kcmmouse.ui)
qt5_add_dbus_interface(common_SRCS ${klauncher_xml} klauncher_iface)
add_library(kcm_input MODULE ${kcm_input_PART_SRCS} ${kcminput_backend_SRCS})
qt5_add_resources( common_SRCS kcm/resources.qrc )
add_library(kcm_input MODULE
${common_SRCS}
${backend_SRCS}
)
target_link_libraries(kcm_input
Qt5::DBus
${backend_LIBS}
KF5::KCMUtils
KF5::I18n
KF5::KIOCore
KF5::KIOWidgets
KF5::KDELibs4Support
${kcminput_backend_LIBS}
KF5::Declarative
Qt5::DBus
Qt5::QuickWidgets
)
install(TARGETS kcm_input DESTINATION ${KDE_INSTALL_PLUGINDIR} )
########### install files ###############
install( FILES mouse.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} )
install( FILES cursor_large_black.pcf.gz cursor_large_white.pcf.gz cursor_small_white.pcf.gz DESTINATION ${KDE_INSTALL_DATADIR}/kcminput )
2002-07-01 Fabian Wolf <fabianw@gmx.net>
* added option to select a white cursor
2000-03-14 David Faure <faure@kde.org>
* mouse.cpp: Added global settings for SC/DC/AutoSelect/ChangeCursor
* mousedefaults.h: New file, to store default values
1998-11-30 Alex Zepeda <garbanzo@hooked.net>
* Makefile.am: Move all the icons into pics/ && pics/mini/
SET(backend_SRCS
${backend_SRCS}
backends/kwin_wl/kwin_wl_backend.cpp
backends/kwin_wl/kwin_wl_device.cpp
)
/*
* Copyright 2018 Roman Gilg <subdiff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kwin_wl_backend.h"
#include "kwin_wl_device.h"
#include <algorithm>
#include <KLocalizedString>
#include <QDBusMessage>
#include <QDBusInterface>
#include <QDBusReply>
#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 reply = m_deviceManager->property("devicesSysNames");
if (reply.isValid()) {
qCDebug(KCM_INPUT) << "Devices list received successfully from KWin.";
devicesSysNames = reply.toStringList();
}
else {
qCCritical(KCM_INPUT) << "Error on receiving device list from KWin.";
m_errorString = i18n("Querying input devices failed. Please reopen this settings module.");
return;
}
for (QString sn : 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_INPUT) << "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_INPUT).nospace() << "Device found: " << dev->name() << " (" << dev->sysName() << ")";
}
}
}
bool KWinWaylandBackend::applyConfig()
{
return std::all_of(m_devices.constBegin(), m_devices.constEnd(),
[] (QObject *t) { return static_cast<KWinWaylandDevice*>(t)->applyConfig(); });
}
bool KWinWaylandBackend::getConfig()
{
return std::all_of(m_devices.constBegin(), m_devices.constEnd(),
[] (QObject *t) { return static_cast<KWinWaylandDevice*>(t)->getConfig(); });
}
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 std::any_of(m_devices.constBegin(), m_devices.constEnd(),
[] (QObject *t) { return static_cast<KWinWaylandDevice*>(t)->isChangedConfig(); });
}
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() || !dev->getConfig()) {
emit deviceAdded(false);
return;
}
m_devices.append(dev);
qCDebug(KCM_INPUT).nospace() << "Device connected: " << dev->name() << " (" << dev->sysName() << ")";
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_INPUT).nospace() << "Device disconnected: " << dev->name() << " (" << dev->sysName() << ")";
int index = it - m_devices.cbegin();
m_devices.removeAt(index);
emit deviceRemoved(index);
}
/*
* Copyright 2017 Xuetian Weng <wengxt@gmail.com>
* Copyright 2018 Roman Gilg <subdiff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -16,38 +16,46 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MOUSEBACKEND_H
#define MOUSEBACKEND_H
#ifndef KWINWAYLANDBACKEND_H
#define KWINWAYLANDBACKEND_H
#include <QObject>
#include "mousesettings.h"
#include "inputbackend.h"
class MouseBackend : public QObject
#include <QVector>
class KWinWaylandDevice;
class QDBusInterface;
class KWinWaylandBackend : public InputBackend
{
Q_OBJECT
protected:
explicit MouseBackend(QObject *parent) : QObject(parent) {}
Q_PROPERTY(int deviceCount READ deviceCount CONSTANT)
public:
static MouseBackend *implementation();
explicit KWinWaylandBackend(QObject *parent = 0);
~KWinWaylandBackend();
bool applyConfig() override;
bool getConfig() override;
bool getDefaultConfig() override;
bool isChangedConfig() const override;
QString errorString() const override { return m_errorString; }
virtual int deviceCount() const override { return m_devices.count(); }
virtual QVector<QObject*> getDevices() const override { return m_devices; }
virtual bool isValid() const = 0;
private Q_SLOTS:
void onDeviceAdded(QString);
void onDeviceRemoved(QString);
// This function will be called before query any property below, thus it
// can be used to save some round trip.
virtual void load() = 0;
virtual void apply(const MouseSettings &settings, bool force) = 0;
private:
void findDevices();
// Return the value from display server or compositor if applicable.
virtual bool supportScrollPolarity() = 0;
virtual QStringList supportedAccelerationProfiles() = 0;
virtual QString accelerationProfile() = 0;
virtual double accelRate() = 0;
virtual int threshold() = 0;
virtual MouseHanded handed() = 0;
QDBusInterface* m_deviceManager;
QVector<QObject*> m_devices;
virtual QString currentCursorTheme() = 0;
virtual void applyCursorTheme(const QString &name, int size) = 0;
QString m_errorString = QString();
};
#endif // MOUSEBACKEND_H
#endif // KWINWAYLANDBACKEND_H
/*
* Copyright 2018 Roman Gilg <subdiff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kwin_wl_device.h"
#include <QDBusInterface>
#include <QDBusError>
#include <QVector>
#include "logging.h"
namespace {
template<typename T>
T valueLoaderPart(QVariant const &reply) { Q_UNUSED(reply); return T(); }
template<>
bool valueLoaderPart(QVariant const &reply) { return reply.toBool(); }
template<>
int valueLoaderPart(QVariant const &reply) { return reply.toInt(); }
template<>
quint32 valueLoaderPart(QVariant const &reply) { return reply.toInt(); }
template<>
qreal valueLoaderPart(QVariant const &reply) { return reply.toReal(); }
template<>
QString valueLoaderPart(QVariant const &reply) { return reply.toString(); }
template<>
Qt::MouseButtons valueLoaderPart(QVariant const &reply) { return static_cast<Qt::MouseButtons>(reply.toInt()); }
}
KWinWaylandDevice::KWinWaylandDevice(QString dbusName)
{
m_iface = new QDBusInterface(QStringLiteral("org.kde.KWin"),
QStringLiteral("/org/kde/KWin/InputDevice/") + dbusName,
QStringLiteral("org.kde.KWin.InputDevice"),
QDBusConnection::sessionBus(),
this);
}
KWinWaylandDevice::~KWinWaylandDevice()
{
delete m_iface;
}
bool KWinWaylandDevice::init()
{
// need to do it here in order to populate combobox and handle events
return valueLoader(m_name) && valueLoader(m_sysName);
}
bool KWinWaylandDevice::getConfig()
{
bool success = true;
// general
success &= valueLoader(m_supportsDisableEvents);
success &= valueLoader(m_enabled);
// advanced
success &= valueLoader(m_supportedButtons);
success &= valueLoader(m_supportsLeftHanded);
success &= valueLoader(m_leftHandedEnabledByDefault);
success &= valueLoader(m_leftHanded);
success &= valueLoader(m_supportsMiddleEmulation);
success &= valueLoader(m_middleEmulationEnabledByDefault);
success &= valueLoader(m_middleEmulation);
// acceleration
success &= valueLoader(m_supportsPointerAcceleration);
success &= valueLoader(m_supportsPointerAccelerationProfileFlat);
success &= valueLoader(m_supportsPointerAccelerationProfileAdaptive);
success &= valueLoader(m_defaultPointerAcceleration);
success &= valueLoader(m_defaultPointerAccelerationProfileFlat);
success &= valueLoader(m_defaultPointerAccelerationProfileAdaptive);
success &= valueLoader(m_pointerAcceleration);
success &= valueLoader(m_pointerAccelerationProfileFlat);
success &= valueLoader(m_pointerAccelerationProfileAdaptive);
// natural scroll
success &= valueLoader(m_supportsNaturalScroll);
success &= valueLoader(m_naturalScrollEnabledByDefault);
success &= valueLoader(m_naturalScroll);
return success;
}
bool KWinWaylandDevice::getDefaultConfig()
{
m_enabled.set(true);
m_leftHanded.set(false);
m_pointerAcceleration.set(m_defaultPointerAcceleration);
m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat);
m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive);
m_middleEmulation.set(m_middleEmulationEnabledByDefault);
m_naturalScroll.set(m_naturalScrollEnabledByDefault);
return true;
}
bool KWinWaylandDevice::applyConfig()
{
QVector<QString> msgs;
msgs << valueWriter(m_enabled)
<< valueWriter(m_leftHanded)
<< valueWriter(m_pointerAcceleration)
<< valueWriter(m_defaultPointerAccelerationProfileFlat)
<< valueWriter(m_defaultPointerAccelerationProfileAdaptive)
<< valueWriter(m_middleEmulation)
<< valueWriter(m_naturalScroll);
bool success = true;
QString error_msg;
for (QString m : msgs) {
if (!m.isNull()) {
qCCritical(KCM_INPUT) << "in error:" << m;
if (!success) {
error_msg.append("\n");
}
error_msg.append(m);
success = false;
}
}
if (!success) {
qCCritical(KCM_INPUT) << error_msg;
}
return success;
}
bool KWinWaylandDevice::isChangedConfig() const
{
return m_enabled.changed() ||
m_leftHanded.changed() ||
m_pointerAcceleration.changed() ||
m_pointerAccelerationProfileFlat.changed() ||
m_pointerAccelerationProfileAdaptive.changed() ||
m_middleEmulation.changed() ||
m_naturalScroll.changed();
}
template<typename T>
QString KWinWaylandDevice::valueWriter(const Prop<T> &prop)
{
if (!prop.changed()) {
return QString();
}
m_iface->setProperty(prop.dbus, prop.val);
QDBusError error = m_iface->lastError();
if (error.isValid()) {
qCCritical(KCM_INPUT) << error.message();
return error.message();
}
return QString();
}
template<typename T>
bool KWinWaylandDevice::valueLoader(Prop<T> &prop)
{
QVariant reply = m_iface->property(prop.dbus);
if (!reply.isValid()) {
qCCritical(KCM_INPUT) << "Error on d-bus read of" << prop.dbus;
prop.avail = false;
return false;
}
prop.avail = true;
T replyValue = valueLoaderPart<T>(reply);
prop.old = replyValue;
prop.val = replyValue;
return true;
}
/*
* Copyright 2018 Roman Gilg <subdiff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KWINWAYLANDDEVICE_H
#define KWINWAYLANDDEVICE_H
#include <QObject>
#include <QString>
class QDBusInterface;