diff --git a/kcms/input/AUTHORS b/kcms/input/AUTHORS deleted file mode 100644 --- a/kcms/input/AUTHORS +++ /dev/null @@ -1,7 +0,0 @@ -Mouse & Keyboard Configuration Modules: - - Pat Dowler (dowler@pt1B1106.FSH.UVic.CA) - -Conversion to kcontrol applet: - - Matthias Hoelzer (hoelzer@physik.uni-wuerzburg.de) diff --git a/kcms/input/CMakeLists.txt b/kcms/input/CMakeLists.txt --- a/kcms/input/CMakeLists.txt +++ b/kcms/input/CMakeLists.txt @@ -5,47 +5,85 @@ # 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 ) - diff --git a/kcms/input/ChangeLog b/kcms/input/ChangeLog deleted file mode 100644 --- a/kcms/input/ChangeLog +++ /dev/null @@ -1,11 +0,0 @@ -2002-07-01 Fabian Wolf - * added option to select a white cursor - -2000-03-14 David Faure - - * mouse.cpp: Added global settings for SC/DC/AutoSelect/ChangeCursor - * mousedefaults.h: New file, to store default values - -1998-11-30 Alex Zepeda - - * Makefile.am: Move all the icons into pics/ && pics/mini/ diff --git a/kcms/input/backends/kwin_wl.cmake b/kcms/input/backends/kwin_wl.cmake new file mode 100644 --- /dev/null +++ b/kcms/input/backends/kwin_wl.cmake @@ -0,0 +1,5 @@ +SET(backend_SRCS + ${backend_SRCS} + backends/kwin_wl/kwin_wl_backend.cpp + backends/kwin_wl/kwin_wl_device.cpp +) diff --git a/kcms/input/backends/kwin_wl/kwin_wl_backend.h b/kcms/input/backends/kwin_wl/kwin_wl_backend.h new file mode 100644 --- /dev/null +++ b/kcms/input/backends/kwin_wl/kwin_wl_backend.h @@ -0,0 +1,61 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 KWINWAYLANDBACKEND_H +#define KWINWAYLANDBACKEND_H + +#include "inputbackend.h" + +#include + +class KWinWaylandDevice; +class QDBusInterface; + +class KWinWaylandBackend : public InputBackend +{ + Q_OBJECT + + Q_PROPERTY(int deviceCount READ deviceCount CONSTANT) + +public: + 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 getDevices() const override { return m_devices; } + +private Q_SLOTS: + void onDeviceAdded(QString); + void onDeviceRemoved(QString); + +private: + void findDevices(); + + QDBusInterface* m_deviceManager; + QVector m_devices; + + QString m_errorString = QString(); +}; + +#endif // KWINWAYLANDBACKEND_H diff --git a/kcms/input/backends/kwin_wl/kwin_wl_backend.cpp b/kcms/input/backends/kwin_wl/kwin_wl_backend.cpp new file mode 100644 --- /dev/null +++ b/kcms/input/backends/kwin_wl/kwin_wl_backend.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 + +#include + +#include +#include +#include +#include + +#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(t)->applyConfig(); }); +} + +bool KWinWaylandBackend::getConfig() +{ + return std::all_of(m_devices.constBegin(), m_devices.constEnd(), + [] (QObject *t) { return static_cast(t)->getConfig(); }); +} + +bool KWinWaylandBackend::getDefaultConfig() +{ + return std::all_of(m_devices.constBegin(), m_devices.constEnd(), + [] (QObject *t) { return static_cast(t)->getDefaultConfig(); }); +} + +bool KWinWaylandBackend::isChangedConfig() const +{ + return std::any_of(m_devices.constBegin(), m_devices.constEnd(), + [] (QObject *t) { return static_cast(t)->isChangedConfig(); }); +} + +void KWinWaylandBackend::onDeviceAdded(QString sysName) +{ + if (std::any_of(m_devices.constBegin(), m_devices.constEnd(), + [sysName] (QObject *t) { return static_cast(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::const_iterator it = std::find_if(m_devices.constBegin(), m_devices.constEnd(), + [sysName] (QObject *t) { return static_cast(t)->sysName() == sysName; }); + if (it == m_devices.cend()) { + return; + } + + KWinWaylandDevice *dev = static_cast(*it); + qCDebug(KCM_INPUT).nospace() << "Device disconnected: " << dev->name() << " (" << dev->sysName() << ")"; + + int index = it - m_devices.cbegin(); + m_devices.removeAt(index); + emit deviceRemoved(index); +} diff --git a/kcms/input/backends/kwin_wl/kwin_wl_device.h b/kcms/input/backends/kwin_wl/kwin_wl_device.h new file mode 100644 --- /dev/null +++ b/kcms/input/backends/kwin_wl/kwin_wl_device.h @@ -0,0 +1,264 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 +#include + +class QDBusInterface; + +class KWinWaylandDevice : public QObject +{ + Q_OBJECT + + // + // general + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(bool supportsDisableEvents READ supportsDisableEvents CONSTANT) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + + // + // advanced + Q_PROPERTY(Qt::MouseButtons supportedButtons READ supportedButtons CONSTANT) + + Q_PROPERTY(bool supportsLeftHanded READ supportsLeftHanded CONSTANT) + Q_PROPERTY(bool leftHandedEnabledByDefault READ leftHandedEnabledByDefault CONSTANT) + Q_PROPERTY(bool leftHanded READ isLeftHanded WRITE setLeftHanded NOTIFY leftHandedChanged) + + Q_PROPERTY(bool supportsMiddleEmulation READ supportsMiddleEmulation CONSTANT) + Q_PROPERTY(bool middleEmulationEnabledByDefault READ middleEmulationEnabledByDefault CONSTANT) + Q_PROPERTY(bool middleEmulation READ isMiddleEmulation WRITE setMiddleEmulation NOTIFY middleEmulationChanged) + + // + // acceleration speed and profile + Q_PROPERTY(bool supportsPointerAcceleration READ supportsPointerAcceleration CONSTANT) + Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration NOTIFY pointerAccelerationChanged) + + Q_PROPERTY(bool supportsPointerAccelerationProfileFlat READ supportsPointerAccelerationProfileFlat CONSTANT) + Q_PROPERTY(bool defaultPointerAccelerationProfileFlat READ defaultPointerAccelerationProfileFlat CONSTANT) + Q_PROPERTY(bool pointerAccelerationProfileFlat READ pointerAccelerationProfileFlat WRITE setPointerAccelerationProfileFlat NOTIFY pointerAccelerationProfileChanged) + + Q_PROPERTY(bool supportsPointerAccelerationProfileAdaptive READ supportsPointerAccelerationProfileAdaptive CONSTANT) + Q_PROPERTY(bool defaultPointerAccelerationProfileAdaptive READ defaultPointerAccelerationProfileAdaptive CONSTANT) + Q_PROPERTY(bool pointerAccelerationProfileAdaptive READ pointerAccelerationProfileAdaptive WRITE setPointerAccelerationProfileAdaptive NOTIFY pointerAccelerationProfileChanged) + + // + // scrolling + Q_PROPERTY(bool supportsNaturalScroll READ supportsNaturalScroll CONSTANT) + Q_PROPERTY(bool naturalScrollEnabledByDefault READ naturalScrollEnabledByDefault CONSTANT) + Q_PROPERTY(bool naturalScroll READ isNaturalScroll WRITE setNaturalScroll NOTIFY naturalScrollChanged) + +public: + KWinWaylandDevice(QString dbusName); + ~KWinWaylandDevice() override; + + bool init(); + + bool getConfig(); + bool getDefaultConfig(); + bool applyConfig(); + bool isChangedConfig() const; + + // + // general + QString name() const { + return m_name.val; + } + QString sysName() const { + return m_sysName.val; + } + bool supportsDisableEvents() const { + return m_supportsDisableEvents.val; + } + void setEnabled(bool enabled) { + m_enabled.set(enabled); + } + bool isEnabled() const { + return m_enabled.val; + } + Qt::MouseButtons supportedButtons() const { + return m_supportedButtons.val; + } + + // + // advanced + bool supportsLeftHanded() const { + return m_supportsLeftHanded.val; + } + bool leftHandedEnabledByDefault() const { + return m_leftHandedEnabledByDefault.val; + } + bool isLeftHanded() const { + return m_leftHanded.val; + } + void setLeftHanded(bool set) { + m_leftHanded.set(set); + } + + bool supportsMiddleEmulation() const { + return m_supportsMiddleEmulation.val; + } + bool middleEmulationEnabledByDefault() const { + return m_middleEmulationEnabledByDefault.val; + } + bool isMiddleEmulation() const { + return m_middleEmulation.val; + } + void setMiddleEmulation(bool set) { + m_middleEmulation.set(set); + } + + // + // acceleration speed and profile + bool supportsPointerAcceleration() const { + return m_supportsPointerAcceleration.val; + } + qreal pointerAcceleration() const { + return m_pointerAcceleration.val; + } + void setPointerAcceleration(qreal acceleration) { + m_pointerAcceleration.set(acceleration); + } + + bool supportsPointerAccelerationProfileFlat() const { + return m_supportsPointerAccelerationProfileFlat.val; + } + bool defaultPointerAccelerationProfileFlat() const { + return m_defaultPointerAccelerationProfileFlat.val; + } + bool pointerAccelerationProfileFlat() const { + return m_pointerAccelerationProfileFlat.val; + } + void setPointerAccelerationProfileFlat(bool set) { + m_pointerAccelerationProfileFlat.set(set); + } + + bool supportsPointerAccelerationProfileAdaptive() const { + return m_supportsPointerAccelerationProfileAdaptive.val; + } + bool defaultPointerAccelerationProfileAdaptive() const { + return m_defaultPointerAccelerationProfileAdaptive.val; + } + bool pointerAccelerationProfileAdaptive() const { + return m_pointerAccelerationProfileAdaptive.val; + } + void setPointerAccelerationProfileAdaptive(bool set) { + m_pointerAccelerationProfileAdaptive.set(set); + } + + // + // scrolling + bool supportsNaturalScroll() const { + return m_supportsNaturalScroll.val; + } + bool naturalScrollEnabledByDefault() const { + return m_naturalScrollEnabledByDefault.val; + } + bool isNaturalScroll() const { + return m_naturalScroll.val; + } + void setNaturalScroll(bool set) { + m_naturalScroll.set(set); + } + +Q_SIGNALS: + void leftHandedChanged(); + void pointerAccelerationChanged(); + void pointerAccelerationProfileChanged(); + void enabledChanged(); + void middleEmulationChanged(); + void naturalScrollChanged(); + +private: + template + struct Prop { + explicit Prop(const QByteArray &dbusName) + : dbus(dbusName) + {} + + void set(T newVal) { + if (avail && val != newVal) { + val = newVal; + } + } + void set(const Prop &p) { + if (avail && val != p.val) { + val = p.val; + } + } + bool changed() const { + return avail && (old != val); + } + + QByteArray dbus; + bool avail; + T old; + T val; + }; + + template + bool valueLoader(Prop &prop); + + template + QString valueWriter(const Prop &prop); + + // + // general + Prop m_name = Prop("name"); + Prop m_sysName = Prop("sysName"); + Prop m_supportsDisableEvents = Prop("supportsDisableEvents"); + Prop m_enabled = Prop("enabled"); + + // + // advanced + Prop m_supportedButtons = Prop("supportedButtons"); + + Prop m_supportsLeftHanded = Prop("supportsLeftHanded"); + Prop m_leftHandedEnabledByDefault = Prop("leftHandedEnabledByDefault"); + Prop m_leftHanded = Prop("leftHanded"); + + Prop m_supportsMiddleEmulation = Prop("supportsMiddleEmulation"); + Prop m_middleEmulationEnabledByDefault = Prop("middleEmulationEnabledByDefault"); + Prop m_middleEmulation = Prop("middleEmulation"); + + // + // acceleration speed and profile + Prop m_supportsPointerAcceleration = Prop("supportsPointerAcceleration"); + Prop m_defaultPointerAcceleration = Prop("defaultPointerAcceleration"); + Prop m_pointerAcceleration = Prop("pointerAcceleration"); + + Prop m_supportsPointerAccelerationProfileFlat = Prop("supportsPointerAccelerationProfileFlat"); + Prop m_defaultPointerAccelerationProfileFlat = Prop("defaultPointerAccelerationProfileFlat"); + Prop m_pointerAccelerationProfileFlat = Prop("pointerAccelerationProfileFlat"); + + Prop m_supportsPointerAccelerationProfileAdaptive = Prop("supportsPointerAccelerationProfileAdaptive"); + Prop m_defaultPointerAccelerationProfileAdaptive = Prop("defaultPointerAccelerationProfileAdaptive"); + Prop m_pointerAccelerationProfileAdaptive = Prop("pointerAccelerationProfileAdaptive"); + + // + // scrolling + Prop m_supportsNaturalScroll = Prop("supportsNaturalScroll"); + Prop m_naturalScrollEnabledByDefault = Prop("naturalScrollEnabledByDefault"); + Prop m_naturalScroll = Prop("naturalScroll"); + + QDBusInterface *m_iface; +}; + +#endif // KWINWAYLANDDEVICE_H diff --git a/kcms/input/backends/kwin_wl/kwin_wl_device.cpp b/kcms/input/backends/kwin_wl/kwin_wl_device.cpp new file mode 100644 --- /dev/null +++ b/kcms/input/backends/kwin_wl/kwin_wl_device.cpp @@ -0,0 +1,192 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 +#include +#include + +#include "logging.h" + +namespace { +template +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(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 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 +QString KWinWaylandDevice::valueWriter(const Prop &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 +bool KWinWaylandDevice::valueLoader(Prop &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(reply); + + prop.old = replyValue; + prop.val = replyValue; + return true; +} diff --git a/kcms/input/backends/x11.cmake b/kcms/input/backends/x11.cmake --- a/kcms/input/backends/x11.cmake +++ b/kcms/input/backends/x11.cmake @@ -1,43 +1,30 @@ # // krazy:excludeall=copyright,license -set(kcminput_backend_SRCS - ${kcminput_backend_SRCS} - backends/x11/x11mousebackend.cpp +include_directories( + ${X11_X11_INCLUDE_PATH} + ${X11_Xinput_INCLUDE_PATH} + ${Evdev_INCLUDE_DIRS} + ${XORGLIBINPUT_INCLUDE_DIRS} ) -set(kcminput_backend_LIBS +set(backend_SRCS + ${backend_SRCS} + backends/x11/evdev_settings.cpp + backends/x11/x11_backend.cpp +) + +set(backend_LIBS + ${backend_LIBS} + KF5::WindowSystem Qt5::X11Extras ${X11_X11_LIB} ${X11_Xinput_LIB} - ${kcminput_backend_LIBS} ) -include_directories(${X11_X11_INCLUDE_PATH} - ${X11_Xinput_INCLUDE_PATH} - ${Evdev_INCLUDE_DIRS} - ${XORGLIBINPUT_INCLUDE_DIRS}) - if (X11_Xcursor_FOUND) - set(kcminput_backend_LIBS + set(backend_LIBS ${X11_Xcursor_LIB} - ${kcminput_backend_LIBS} + ${backend_LIBS} ) include_directories(${X11_Xcursor_INCLUDE_PATH}) endif () - - -set(kapplymousetheme_SRCS - backends/x11/kapplymousetheme.cpp) - -add_executable(kapplymousetheme ${kapplymousetheme_SRCS} ${kcminput_backend_SRCS}) - -target_link_libraries(kapplymousetheme - Qt5::Gui - Qt5::DBus - KF5::CoreAddons - KF5::ConfigCore - KF5::I18n - ${kcminput_backend_LIBS} -) - -install(TARGETS kapplymousetheme ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/kcms/input/mousesettings.h b/kcms/input/backends/x11/evdev_settings.h rename from kcms/input/mousesettings.h rename to kcms/input/backends/x11/evdev_settings.h --- a/kcms/input/mousesettings.h +++ b/kcms/input/backends/x11/evdev_settings.h @@ -1,5 +1,6 @@ /* * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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,28 +17,28 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef MOUSESETTINGS_H -#define MOUSESETTINGS_H +#ifndef EVDEVSETTINGS_H +#define EVDEVSETTINGS_H #include -class MouseBackend; +class X11Backend; -enum class MouseHanded { +enum class Handed { Right = 0, Left = 1, NotSupported = -1 }; -struct MouseSettings +struct EvdevSettings { - void save(KConfig *); - void load(KConfig *, MouseBackend*); - void apply(MouseBackend*, bool force = false); + void save(); + void load(X11Backend *); + void apply(X11Backend *, bool force = false); bool handedEnabled; bool handedNeedsApply; - MouseHanded handed; + Handed handed; double accelRate; int thresholdMove; int doubleClickInterval; @@ -49,4 +50,4 @@ QString currentAccelProfile; }; -#endif // MOUSESETTINGS_H +#endif // EVDEVSETTINGS_H diff --git a/kcms/input/mousesettings.cpp b/kcms/input/backends/x11/evdev_settings.cpp rename from kcms/input/mousesettings.cpp rename to kcms/input/backends/x11/evdev_settings.cpp --- a/kcms/input/mousesettings.cpp +++ b/kcms/input/backends/x11/evdev_settings.cpp @@ -1,5 +1,6 @@ /* * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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 @@ -15,47 +16,46 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#include "mousesettings.h" - -#include "mousebackend.h" +#include "evdev_settings.h" +#include "x11_backend.h" #include #include #include #include #include "../migrationlib/kdelibs4config.h" -void MouseSettings::apply(MouseBackend* backend, bool force) +void EvdevSettings::apply(X11Backend *backend, bool force) { if (!backend) { return; } - backend->apply(*this, force); + backend->apply(force); handedNeedsApply = false; } -void MouseSettings::load(KConfig *config, MouseBackend *backend) +void EvdevSettings::load(X11Backend *backend) { + KConfig config("kcminputrc"); + // TODO: what's a good threshold default value int threshold = 0; - handed = MouseHanded::Right; + handed = Handed::Right; double accel = 1.0; QString profile; if (backend) { - backend->load(); auto handedOnServer = backend->handed(); - handedEnabled = handedOnServer != MouseHanded::NotSupported; + handedEnabled = handedOnServer != Handed::NotSupported; if (handedEnabled) { handed = handedOnServer; } accel = backend->accelRate(); threshold = backend->threshold(); profile = backend->accelerationProfile(); } - KConfigGroup group = config->group("Mouse"); + KConfigGroup group = config.group("Mouse"); double a = group.readEntry("Acceleration", -1.0); if (a == -1) accelRate = accel; @@ -70,18 +70,18 @@ QString key = group.readEntry("MouseButtonMapping"); if (key == "RightHanded") - handed = MouseHanded::Right; + handed = Handed::Right; else if (key == "LeftHanded") - handed = MouseHanded::Left; + handed = Handed::Left; reverseScrollPolarity = group.readEntry("ReverseScrollPolarity", false); currentAccelProfile = group.readEntry("AccelerationProfile"); if (currentAccelProfile.isEmpty()) { currentAccelProfile = profile; } handedNeedsApply = false; // SC/DC/AutoSelect/ChangeCursor - group = config->group("KDE"); + group = config.group("KDE"); doubleClickInterval = group.readEntry("DoubleClickInterval", 400); dragStartTime = group.readEntry("StartDragTime", 500); dragStartDist = group.readEntry("StartDragDist", 4); @@ -112,13 +112,13 @@ QDBusConnection::sessionBus().send(message); } -void MouseSettings::save(KConfig *config) +void EvdevSettings::save() { KSharedConfig::Ptr kcminputProfile = KSharedConfig::openConfig("kcminputrc"); KConfigGroup kcminputGroup(kcminputProfile, "Mouse"); kcminputGroup.writeEntry("Acceleration",accelRate); kcminputGroup.writeEntry("Threshold",thresholdMove); - if (handed == MouseHanded::Right) { + if (handed == Handed::Right) { kcminputGroup.writeEntry("MouseButtonMapping",QString("RightHanded")); } else { kcminputGroup.writeEntry("MouseButtonMapping",QString("LeftHanded")); @@ -136,7 +136,7 @@ group.writeEntry("SingleClick", singleClick, KConfig::Persistent); group.sync(); - config->sync(); + kcminputProfile->sync(); Kdelibs4SharedConfig::syncConfigGroup(QLatin1String("Mouse"), "kcminputrc"); Kdelibs4SharedConfig::syncConfigGroup(QLatin1String("KDE"), "kdeglobals"); diff --git a/kcms/input/backends/x11/x11mousebackend.h b/kcms/input/backends/x11/x11_backend.h rename from kcms/input/backends/x11/x11mousebackend.h rename to kcms/input/backends/x11/x11_backend.h --- a/kcms/input/backends/x11/x11mousebackend.h +++ b/kcms/input/backends/x11/x11_backend.h @@ -1,5 +1,6 @@ /* * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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,34 +17,48 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef XLIBMOUSEBACKEND_H -#define XLIBMOUSEBACKEND_H +#ifndef X11BACKEND_H +#define X11BACKEND_H -#include "mousebackend.h" +#include "inputbackend.h" +#include "evdev_settings.h" #include #include -class X11MouseBackend : public MouseBackend +class ConfigPlugin; + +class X11Backend : public InputBackend { Q_OBJECT public: - X11MouseBackend(QObject *parent = nullptr); - ~X11MouseBackend(); + X11Backend(QObject *parent = nullptr); + ~X11Backend(); bool isValid() const override { return m_dpy != nullptr; } void load() override; - bool supportScrollPolarity() override; - QStringList supportedAccelerationProfiles() override; - QString accelerationProfile() override; - double accelRate() override; - MouseHanded handed() override; - int threshold() override; - void apply(const MouseSettings & settings, bool force) override; - QString currentCursorTheme() override; - void applyCursorTheme(const QString &name, int size) override; + void apply(bool force = false); + + EvdevSettings* settings() { + return m_settings; + } + + bool supportScrollPolarity(); + QStringList supportedAccelerationProfiles(); + QString accelerationProfile(); + double accelRate(); + Handed handed(); + int threshold(); + + QString currentCursorTheme(); + void applyCursorTheme(const QString &name, int size); + +Q_SIGNALS: + void mouseStateChanged(); + void mousesChanged(); + void mouseReset(); private: void initAtom(); @@ -63,13 +78,14 @@ // We may still need to do something on non-X11 platform due to Xwayland. Display* m_dpy; bool m_platformX11; + EvdevSettings *m_settings = nullptr; int m_numButtons = 1; - MouseHanded m_handed = MouseHanded::NotSupported; + Handed m_handed = Handed::NotSupported; double m_accelRate = 1.0; int m_threshold = 0; int m_middleButton = -1; QStringList m_supportedAccelerationProfiles; QString m_accelerationProfile; }; -#endif // XLIBMOUSEBACKEND_H +#endif // X11BACKEND_H diff --git a/kcms/input/backends/x11/x11mousebackend.cpp b/kcms/input/backends/x11/x11_backend.cpp rename from kcms/input/backends/x11/x11mousebackend.cpp rename to kcms/input/backends/x11/x11_backend.cpp --- a/kcms/input/backends/x11/x11mousebackend.cpp +++ b/kcms/input/backends/x11/x11_backend.cpp @@ -1,5 +1,6 @@ /* * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "x11mousebackend.h" -#include "mousesettings.h" +#include "x11_backend.h" + #include #include @@ -33,7 +34,7 @@ #include #include #ifdef HAVE_XCURSOR -# include +#include #include #endif @@ -82,19 +83,24 @@ XFreeDeviceList(info); } -X11MouseBackend::X11MouseBackend(QObject* parent) : MouseBackend(parent), m_dpy(nullptr) +X11Backend::X11Backend(QObject* parent) + : InputBackend(parent) { + m_mode = InputBackendMode::XEvdev; + m_platformX11 = QX11Info::isPlatformX11(); if (m_platformX11) { m_dpy = QX11Info::display(); } else { + // TODO: remove this - not needed anymore with Wayland backend! // let's hope we have a compatibility system like Xwayland ready m_dpy = XOpenDisplay(nullptr); } + m_settings = new EvdevSettings(); initAtom(); } -void X11MouseBackend::initAtom() +void X11Backend::initAtom() { if (!m_dpy) { return; @@ -112,44 +118,45 @@ } -X11MouseBackend::~X11MouseBackend() +X11Backend::~X11Backend() { if (!m_platformX11 && m_dpy) { XCloseDisplay(m_dpy); } + delete m_settings; } -bool X11MouseBackend::supportScrollPolarity() +bool X11Backend::supportScrollPolarity() { return m_numButtons >= 5; } -QStringList X11MouseBackend::supportedAccelerationProfiles() +QStringList X11Backend::supportedAccelerationProfiles() { return m_supportedAccelerationProfiles; } -QString X11MouseBackend::accelerationProfile() +QString X11Backend::accelerationProfile() { return m_accelerationProfile; } -double X11MouseBackend::accelRate() +double X11Backend::accelRate() { return m_accelRate; } -MouseHanded X11MouseBackend::handed() +Handed X11Backend::handed() { return m_handed; } -int X11MouseBackend::threshold() +int X11Backend::threshold() { return m_threshold; } -void X11MouseBackend::load() +void X11Backend::load() { if (!m_dpy) { return; @@ -165,20 +172,20 @@ m_numButtons = XGetPointerMapping(m_dpy, map, 256); m_middleButton = -1; - m_handed = MouseHanded::NotSupported; + m_handed = Handed::NotSupported; // ## keep this in sync with KGlobalSettings::mouseSettings if (m_numButtons == 2) { if (map[0] == 1 && map[1] == 2) { - m_handed = MouseHanded::Right; + m_handed = Handed::Right; } else if (map[0] == 2 && map[1] == 1) { - m_handed = MouseHanded::Left; + m_handed = Handed::Left; } } else if (m_numButtons >= 3) { m_middleButton = map[1]; if (map[0] == 1 && map[2] == 3) { - m_handed = MouseHanded::Right; + m_handed = Handed::Right; } else if (map[0] == 3 && map[2] == 1) { - m_handed = MouseHanded::Left; + m_handed = Handed::Left; } } @@ -239,29 +246,31 @@ } else if (flatEnabled) { m_accelerationProfile = PROFILE_FLAT; } + + m_settings->load(this); } -void X11MouseBackend::apply(const MouseSettings& settings, bool force) +void X11Backend::apply(bool force) { // 256 might seems extreme, but X has already been known to return 32, // and we don't want to truncate things. Xlib limits the table to 256 bytes, // so it's a good upper bound.. unsigned char map[256]; XGetPointerMapping(m_dpy, map, 256); - if (settings.handedEnabled && (settings.handedNeedsApply || force)) { + if (m_settings->handedEnabled && (m_settings->handedNeedsApply || force)) { if (m_numButtons == 1) { map[0] = (unsigned char) 1; } else if (m_numButtons == 2) { - if (settings.handed == MouseHanded::Right) { + if (m_settings->handed == Handed::Right) { map[0] = (unsigned char) 1; map[1] = (unsigned char) 3; } else { map[0] = (unsigned char) 3; map[1] = (unsigned char) 1; } } else { // 3 buttons and more - if (settings.handed == MouseHanded::Right) { + if (m_settings->handed == Handed::Right) { map[0] = (unsigned char) 1; map[1] = (unsigned char) m_middleButton; map[2] = (unsigned char) 3; @@ -282,30 +291,30 @@ // apply reverseScrollPolarity for all non-touchpad pointer, touchpad // are belong to kcm touchpad. - XIForallPointerDevices(m_dpy, [this, &settings](XDeviceInfo * info) { + XIForallPointerDevices(m_dpy, [this](XDeviceInfo * info) { int deviceid = info->id; if (info->type == m_touchpadAtom) { return; } - if (libinputApplyReverseScroll(deviceid, settings.reverseScrollPolarity)) { + if (libinputApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity)) { return; } - evdevApplyReverseScroll(deviceid, settings.reverseScrollPolarity); + evdevApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity); }); } XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { - libinputApplyAccelerationProfile(info->deviceid, settings.currentAccelProfile); + libinputApplyAccelerationProfile(info->deviceid, m_settings->currentAccelProfile); }); XChangePointerControl(m_dpy, - true, true, int(qRound(settings.accelRate * 10)), 10, settings.thresholdMove); + true, true, int(qRound(m_settings->accelRate * 10)), 10, m_settings->thresholdMove); XFlush(m_dpy); } -QString X11MouseBackend::currentCursorTheme() +QString X11Backend::currentCursorTheme() { if (!m_dpy) { return QString(); @@ -320,7 +329,7 @@ return QFile::decodeName(name); } -void X11MouseBackend::applyCursorTheme(const QString& theme, int size) +void X11Backend::applyCursorTheme(const QString& theme, int size) { #ifdef HAVE_XCURSOR @@ -344,7 +353,7 @@ #endif } -bool X11MouseBackend::evdevApplyReverseScroll(int deviceid, bool reverse) +bool X11Backend::evdevApplyReverseScroll(int deviceid, bool reverse) { // Check atom availability first. if (m_evdevWheelEmulationAtom == None || m_evdevScrollDistanceAtom == None || @@ -413,7 +422,7 @@ return true; } -bool X11MouseBackend::libinputApplyReverseScroll(int deviceid, bool reverse) +bool X11Backend::libinputApplyReverseScroll(int deviceid, bool reverse) { // Check atom availability first. if (m_libinputNaturalScrollAtom == None) { @@ -444,7 +453,7 @@ return true; } -void X11MouseBackend::libinputApplyAccelerationProfile(int deviceid, QString profile) +void X11Backend::libinputApplyAccelerationProfile(int deviceid, QString profile) { // Check atom availability first. if (m_libinputAccelProfileAvailableAtom == None || m_libinputAccelProfileEnabledAtom == None) { diff --git a/kcms/input/inputbackend.h b/kcms/input/inputbackend.h new file mode 100644 --- /dev/null +++ b/kcms/input/inputbackend.h @@ -0,0 +1,73 @@ +/* + * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg + * + * 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 INPUTBACKEND_H +#define INPUTBACKEND_H + +#include +#include +#include + +class ConfigPlugin; + +enum class InputBackendMode { + KWinWayland = 0, + XLibinput = 1, // TODO + XEvdev = 2 +}; + +class InputBackend : public QObject +{ + Q_OBJECT + +protected: + explicit InputBackend(QObject *parent) : QObject(parent) {} + InputBackendMode m_mode; + +public: + static InputBackend *implementation(QObject *parent = nullptr); + + InputBackendMode mode() { + return m_mode; + } + + virtual bool isValid() const { return false; } + + virtual void load() {} + + virtual bool applyConfig(const QVariantHash &) { return false; } + virtual bool getConfig(QVariantHash &) { return false; } + + virtual bool applyConfig() { return false; } + virtual bool getConfig() { return false; } + + virtual bool getDefaultConfig() { return false; } + virtual bool isChangedConfig() const { return false; } + + virtual QString errorString() const { return QString(); } + + virtual int deviceCount() const { return 0; } + virtual QVector getDevices() const { return QVector(); } + +Q_SIGNALS: + void deviceAdded(bool success); + void deviceRemoved(int index); +}; + +#endif // INPUTBACKEND_H diff --git a/kcms/input/mousebackend.cpp b/kcms/input/inputbackend.cpp rename from kcms/input/mousebackend.cpp rename to kcms/input/inputbackend.cpp --- a/kcms/input/mousebackend.cpp +++ b/kcms/input/inputbackend.cpp @@ -1,5 +1,6 @@ /* * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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 @@ -15,29 +16,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "inputbackend.h" -#include "mousebackend.h" - -#include "backends/x11/x11mousebackend.h" +#include "backends/x11/x11_backend.h" +#include "backends/kwin_wl/kwin_wl_backend.h" #include "logging.h" -#include -#include - -#include +#include -MouseBackend *MouseBackend::implementation() +InputBackend *InputBackend::implementation(QObject *parent) { - //There are multiple possible backends, always use X11 backend for now. - static QThreadStorage> backend; - if (!backend.hasLocalData()) { + //There are multiple possible backends + if (KWindowSystem::isPlatformX11()) { qCDebug(KCM_INPUT) << "Using X11 backend"; - backend.setLocalData(QSharedPointer(new X11MouseBackend)); + return new X11Backend(parent); + } + else if (KWindowSystem::isPlatformWayland()) { + qCDebug(KCM_INPUT) << "Using KWin+Wayland backend"; + return new KWinWaylandBackend(parent); + } + else { + qCCritical(KCM_INPUT) << "Not able to select appropriate backend."; + return nullptr; } - return backend.localData().data(); - -#if 0 - qCCritical(KCM_INPUT) << "Not able to select appropriate backend."; - return nullptr; -#endif } diff --git a/kcms/input/backends/x11/kapplymousetheme.cpp b/kcms/input/kapplymousetheme.cpp rename from kcms/input/backends/x11/kapplymousetheme.cpp rename to kcms/input/kapplymousetheme.cpp --- a/kcms/input/backends/x11/kapplymousetheme.cpp +++ b/kcms/input/kapplymousetheme.cpp @@ -4,6 +4,7 @@ * Copyright (c) 1999 Matthias Hoelzer-Kluepfel * Copyright (c) 2005 Lubos Lunak * Copyright (c) 2017 Xuetian Weng + * Copyright (c) 2018 Roman Gilg * * Requires the Qt widget libraries, available at no cost at * http://www.troll.no/ @@ -22,10 +23,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "backends/x11/x11_backend.h" + +#include + #include #include -#include "mousebackend.h" +#include int main( int argc, char* argv[] ) { @@ -35,20 +40,27 @@ return 1; QString theme = QFile::decodeName(argv[ 1 ]); QString size = QFile::decodeName(argv[ 2 ]); - auto backend = MouseBackend::implementation(); - if (!backend || !backend->isValid()) { + + if (!KWindowSystem::isPlatformX11()) { + qDebug() << "X11 backend not detected. Exit."; + return 2; + } + + X11Backend backend; + + if (!backend.isValid()) { return 2; } // Note: If you update this code, update main.cpp as well. // use a default value for theme only if it's not configured at all, not even in X resources - if(theme.isEmpty() && backend->currentCursorTheme().isEmpty()) + if(theme.isEmpty() && backend.currentCursorTheme().isEmpty()) { theme = "breeze_cursors"; ret = 10; // means to switch to default } - backend->applyCursorTheme(theme, size.toInt()); + backend.applyCursorTheme(theme, size.toInt()); return ret; } diff --git a/kcms/input/kcm/configcontainer.h b/kcms/input/kcm/configcontainer.h new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/configcontainer.h @@ -0,0 +1,53 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 CONFIGCONTAINER_H +#define CONFIGCONTAINER_H + +#include + +class ConfigPlugin; + +class ConfigContainer : public KCModule +{ + Q_OBJECT + +public: + explicit ConfigContainer(QWidget *parent, + const QVariantList &args = QVariantList()); + + QSize minimumSizeHint() const override; + QSize sizeHint() const override; + void resizeEvent(QResizeEvent *event) override; + + void load() override; + void save() override; + void defaults() override; + + void kcmLoad() { KCModule::load(); } + void kcmSave() { KCModule::save(); } + void kcmDefaults() { KCModule::defaults(); } + +protected: + void hideEvent(QHideEvent *) override; + +private: + ConfigPlugin* m_plugin = nullptr; +}; + +#endif // CONFIGCONTAINER_H diff --git a/kcms/input/kcm/configcontainer.cpp b/kcms/input/kcm/configcontainer.cpp new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/configcontainer.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 "configcontainer.h" +#include "configplugin.h" +#include "kcm/libinput/libinput_config.h" +#include "kcm/xlib/xlib_config.h" + +#include + +extern "C" +{ + Q_DECL_EXPORT void kcminit_mouse() + { + if (KWindowSystem::isPlatformX11()) { + XlibConfig::kcmInit(); + } + } +} + +ConfigContainer::ConfigContainer(QWidget *parent, const QVariantList &args) + : KCModule(parent, args) +{ + m_plugin = ConfigPlugin::implementation(this); +} + +QSize ConfigContainer::minimumSizeHint() const +{ + return m_plugin->minimumSizeHint(); +} +QSize ConfigContainer::sizeHint() const +{ + return m_plugin->sizeHint(); +} +void ConfigContainer::resizeEvent(QResizeEvent *event) +{ + Q_EMIT changed(false); + m_plugin->resize(this->size()); +} + +void ConfigContainer::load() +{ + m_plugin->load(); +} + +void ConfigContainer::save() +{ + m_plugin->save(); +} + +void ConfigContainer::defaults() +{ + m_plugin->defaults(); +} + +void ConfigContainer::hideEvent(QHideEvent *e) +{ + m_plugin->hideEvent(e); + KCModule::hideEvent(e); +} diff --git a/kcms/input/logging.h b/kcms/input/kcm/configplugin.h rename from kcms/input/logging.h rename to kcms/input/kcm/configplugin.h --- a/kcms/input/logging.h +++ b/kcms/input/kcm/configplugin.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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,10 +16,33 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef KCM_INPUT_LOGGING_H -#define KCM_INPUT_LOGGING_H +#ifndef CONFIGPLUGIN_H +#define CONFIGPLUGIN_H -#include +#include -Q_DECLARE_LOGGING_CATEGORY(KCM_INPUT) -#endif +class ConfigContainer; +class InputBackend; + +class ConfigPlugin : public QWidget +{ + Q_OBJECT + +public: + static ConfigPlugin *implementation(ConfigContainer *parent); + + explicit ConfigPlugin(ConfigContainer *parent); + virtual ~ConfigPlugin() {} + + virtual void load() {} + virtual void save() {} + virtual void defaults() {} + + void hideEvent(QHideEvent *) override {} + +protected: + ConfigContainer *m_parent; + InputBackend *m_backend; +}; + +#endif // CONFIGPLUGIN_H diff --git a/kcms/input/kcm/configplugin.cpp b/kcms/input/kcm/configplugin.cpp new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/configplugin.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 "configplugin.h" +#include "configcontainer.h" +#include "inputbackend.h" + +#include "libinput/libinput_config.h" +#include "xlib/xlib_config.h" + +#include + +ConfigPlugin* ConfigPlugin::implementation(ConfigContainer *parent) +{ + InputBackend *backend = InputBackend::implementation(parent); + InputBackendMode mode = backend->mode(); + + if (mode == InputBackendMode::KWinWayland || mode == InputBackendMode::XLibinput) { + qCDebug(KCM_INPUT) << "With libinput user interface."; + return new LibinputConfig(parent, backend); + } else if (mode == InputBackendMode::XEvdev) { + qCDebug(KCM_INPUT) << "With X11 evdev user interface."; + return new XlibConfig(parent, backend); + } else { + qCCritical(KCM_INPUT) << "Not able to select appropriate backend."; + return nullptr; + } +} + +ConfigPlugin::ConfigPlugin(ConfigContainer *parent) + : QWidget(parent), + m_parent(parent) +{ +} diff --git a/kcms/input/kcm/libinput/components/ExclGroupBox.qml b/kcms/input/kcm/libinput/components/ExclGroupBox.qml new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/libinput/components/ExclGroupBox.qml @@ -0,0 +1,57 @@ +/* + * Copyright 2017 Roman Gilg + * + * 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. + */ + +import QtQuick 2.7 +import QtQuick.Controls 1.4 as Controls + +import org.kde.plasma.core 2.0 as PlasmaCore + +Column { + spacing: units.smallSpacing / 2 + property alias label: textlabel.text + property alias model: repeater.model + property alias current: exlGroupbox.current + + function itemAt(index) { + return repeater.itemAt(index) + } + + Controls.Label { + id: textlabel + } + + Controls.ExclusiveGroup { id: exlGroupbox } + Column { + leftPadding: units.smallSpacing + spacing: units.smallSpacing / 2 + + Repeater { + id: repeater + Controls.RadioButton { + text: modelData + exclusiveGroup: exlGroupbox + + property alias tooltiptext: tooltip.text + + ToolTip { + id: tooltip + } + } + } + } +} diff --git a/kcms/input/mousesettings.h b/kcms/input/kcm/libinput/components/ToolTip.qml rename from kcms/input/mousesettings.h rename to kcms/input/kcm/libinput/components/ToolTip.qml --- a/kcms/input/mousesettings.h +++ b/kcms/input/kcm/libinput/components/ToolTip.qml @@ -1,5 +1,5 @@ /* - * Copyright 2017 Xuetian Weng + * Copyright 2017 Roman Gilg * * 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,37 +16,37 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef MOUSESETTINGS_H -#define MOUSESETTINGS_H - -#include - -class MouseBackend; - -enum class MouseHanded { - Right = 0, - Left = 1, - NotSupported = -1 -}; - -struct MouseSettings -{ - void save(KConfig *); - void load(KConfig *, MouseBackend*); - void apply(MouseBackend*, bool force = false); - - bool handedEnabled; - bool handedNeedsApply; - MouseHanded handed; - double accelRate; - int thresholdMove; - int doubleClickInterval; - int dragStartTime; - int dragStartDist; - bool singleClick; - int wheelScrollLines; - bool reverseScrollPolarity; - QString currentAccelProfile; -}; - -#endif // MOUSESETTINGS_H +import QtQuick 2.7 + +import QtQuick.Controls.Private 1.0 + +MouseArea { + anchors.fill: parent + + property string text: "" + + hoverEnabled: true + acceptedButtons: Qt.NoButton + + onEntered: timer.start() + onExited: timer.killTooltip() + onPositionChanged: timer.resetTooltip() + + Timer { + id: timer + interval: 1000 + onTriggered: { + Tooltip.showText(parent, Qt.point(mouseX, mouseY), text) + } + + function killTooltip() { + stop() + Tooltip.hideText() + } + + function resetTooltip() { + restart() + Tooltip.hideText() + } + } +} diff --git a/kcms/input/kcm/libinput/libinput_config.h b/kcms/input/kcm/libinput/libinput_config.h new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/libinput/libinput_config.h @@ -0,0 +1,59 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 LIBINPUTCONFIG_H +#define LIBINPUTCONFIG_H + +#include "../configplugin.h" + +class QHideEvent; +class QQuickWidget; +class KMessageWidget; + +class LibinputConfig : public ConfigPlugin +{ + Q_OBJECT + +public: + explicit LibinputConfig(ConfigContainer *parent, InputBackend *backend); + virtual ~LibinputConfig() {} + + void load() override; + void save() override; + void defaults() override; + + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + + void hideEvent(QHideEvent *) override {} + +private Q_SLOTS: + void onChange(); + void onDeviceAdded(bool success); + void onDeviceRemoved(int index); + +private: + void hideErrorMessage(); + + QQuickWidget *m_view; + KMessageWidget *m_errorMessage; + + bool m_initError; +}; + +#endif // LIBINPUTCONFIG_H diff --git a/kcms/input/kcm/libinput/libinput_config.cpp b/kcms/input/kcm/libinput/libinput_config.cpp new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/libinput/libinput_config.cpp @@ -0,0 +1,225 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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 "libinput_config.h" +#include "../configcontainer.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "inputbackend.h" + +static QVariant getDeviceList(InputBackend *backend) +{ + return QVariant::fromValue(backend->getDevices().toList()); +} + +LibinputConfig::LibinputConfig(ConfigContainer *parent, InputBackend *backend) + : ConfigPlugin(parent) +{ + m_backend = backend; + + KAboutData* data = new KAboutData(QStringLiteral("kcmmouse"), + i18n("Pointer device KCM"), + QStringLiteral("1.0"), + i18n("System Settings module for managing mice and trackballs."), + KAboutLicense::GPL_V2, + i18n("Copyright 2018 Roman Gilg"), + QString()); + + data->addAuthor(i18n("Roman Gilg"), + i18n("Developer"), + QStringLiteral("subdiff@gmail.com")); + + m_parent->setAboutData(data); + + m_initError = !m_backend->errorString().isNull(); + + m_view = new QQuickWidget(this); + + m_errorMessage = new KMessageWidget(this); + m_errorMessage->setCloseButtonVisible(false); + m_errorMessage->setWordWrap(true); + m_errorMessage->setVisible(false); + + QVBoxLayout *layout = new QVBoxLayout(parent); + + layout->addWidget(m_errorMessage); + layout->addWidget(m_view); + parent->setLayout(layout); + + m_view->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_view->setClearColor(Qt::transparent); + m_view->setAttribute(Qt::WA_AlwaysStackOnTop); + + m_view->rootContext()->setContextProperty("backend", m_backend); + m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); + + KDeclarative::KDeclarative kdeclarative; + kdeclarative.setDeclarativeEngine(m_view->engine()); + kdeclarative.setupBindings(); + m_view->setSource(QUrl("qrc:/libinput/main.qml")); + + if (m_initError) { + m_errorMessage->setMessageType(KMessageWidget::Error); + m_errorMessage->setText(m_backend->errorString()); + QMetaObject::invokeMethod(m_errorMessage, "animatedShow", + Qt::QueuedConnection); + } else { + connect(m_backend, SIGNAL(deviceAdded(bool)), this, SLOT(onDeviceAdded(bool))); + connect(m_backend, SIGNAL(deviceRemoved(int)), this, SLOT(onDeviceRemoved(int))); + connect(m_view->rootObject(), SIGNAL(changeSignal()), this, SLOT(onChange())); + } + + m_view->show(); +} + +QSize LibinputConfig::sizeHint() const +{ + return QQmlProperty::read(m_view->rootObject(), "sizeHint").toSize(); +} + +QSize LibinputConfig::minimumSizeHint() const +{ + return QQmlProperty::read(m_view->rootObject(), "minimumSizeHint").toSize(); +} + +void LibinputConfig::load() +{ + // in case of critical init error in backend, don't try + if (m_initError) { + return; + } + + if (!m_backend->getConfig()) { + m_errorMessage->setMessageType(KMessageWidget::Error); + m_errorMessage->setText(i18n("Error while loading values. See logs for more informations. Please restart this configuration module.")); + m_errorMessage->animatedShow(); + } else { + if (!m_backend->deviceCount()) { + m_errorMessage->setMessageType(KMessageWidget::Information); + m_errorMessage->setText(i18n("No pointer device found. Connect now.")); + m_errorMessage->animatedShow(); + } + } + QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend"); +} + +void LibinputConfig::save() +{ + if (!m_backend->applyConfig()) { + m_errorMessage->setMessageType(KMessageWidget::Error); + m_errorMessage->setText(i18n("Not able to save all changes. See logs for more informations. Please restart this configuration module and try again.")); + m_errorMessage->animatedShow(); + } else { + hideErrorMessage(); + } + // load newly written values + load(); + // in case of error, config still in changed state + emit m_parent->changed(m_backend->isChangedConfig()); +} + +void LibinputConfig::defaults() +{ + // in case of critical init error in backend, don't try + if (m_initError) { + return; + } + + if (!m_backend->getDefaultConfig()) { + m_errorMessage->setMessageType(KMessageWidget::Error); + m_errorMessage->setText(i18n("Error while loading default values. Failed to set some options to their default values.")); + m_errorMessage->animatedShow(); + } + QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend"); + emit m_parent->changed(m_backend->isChangedConfig()); +} + +void LibinputConfig::onChange() +{ + if (!m_backend->deviceCount()) { + return; + } + hideErrorMessage(); + emit m_parent->changed(m_backend->isChangedConfig()); +} + +void LibinputConfig::onDeviceAdded(bool success) +{ + QQuickItem *rootObj = m_view->rootObject(); + + if (!success) { + m_errorMessage->setMessageType(KMessageWidget::Error); + m_errorMessage->setText(i18n("Error while adding newly connected device. Please reconnect it and restart this configuration module.")); + } + + int activeIndex; + if (m_backend->deviceCount() == 1) { + // if no pointer device was connected previously, show the new device and hide the no-device-message + activeIndex = 0; + hideErrorMessage(); + } else { + activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt(); + } + m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); + QMetaObject::invokeMethod(rootObj, "resetModel", Q_ARG(QVariant, activeIndex)); + QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); +} + +void LibinputConfig::onDeviceRemoved(int index) +{ + QQuickItem *rootObj = m_view->rootObject(); + + int activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt(); + if (activeIndex == index) { + m_errorMessage->setMessageType(KMessageWidget::Information); + if (m_backend->deviceCount()) { + m_errorMessage->setText(i18n("Pointer device disconnected. Closed its setting dialog.")); + } else { + m_errorMessage->setText(i18n("Pointer device disconnected. No other devices found.")); + } + m_errorMessage->animatedShow(); + activeIndex = 0; + } else { + if (index < activeIndex) { + activeIndex--; + } + } + m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); + QMetaObject::invokeMethod(m_view->rootObject(), "resetModel", Q_ARG(QVariant, activeIndex)); + QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); + + emit m_parent->changed(m_backend->isChangedConfig()); +} + +void LibinputConfig::hideErrorMessage() +{ + if (m_errorMessage->isVisible()) { + m_errorMessage->animatedHide(); + } +} diff --git a/kcms/input/kcm/libinput/main.qml b/kcms/input/kcm/libinput/main.qml new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/libinput/main.qml @@ -0,0 +1,323 @@ +/* + * Copyright 2018 Roman Gilg + * + * 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. + */ + +import QtQuick 2.7 +import QtQuick.Controls 1.4 as Controls +import QtQuick.Layouts 1.3 as Layouts +import QtQuick.Controls.Styles 1.4 as Styles + +import org.kde.plasma.core 2.0 as PlasmaCore + +import "components" + +Item { + id: root + + property size sizeHint: Qt.size(maincol.width, maincol.height) + property size minimumSizeHint: Qt.size(maincol.width/2, deviceSelector.height) + property alias deviceIndex: deviceSelector.currentIndex + signal changeSignal() + + property QtObject device + property int deviceCount: backend.deviceCount + + property bool loading: false + + function resetModel(index) { + deviceCount = backend.deviceCount + maincol.enabled = deviceCount + deviceSelector.enabled = deviceCount > 1 + + loading = true + if (deviceCount) { + device = deviceModel[index] + deviceSelector.model = deviceModel + deviceSelector.currentIndex = index + console.log("Configuration of device '" + + (index + 1) + " : " + device.name + "' opened") + } else { + deviceSelector.model = [""] + console.log("No device found") + } + loading = false + } + + function syncValuesFromBackend() { + loading = true + + deviceEnabled.load() + leftHanded.load() + accelSpeed.load() + accelProfile.load() + + naturalScroll.load() + + loading = false + } + + Controls.ScrollView { + anchors.fill: parent + + Layouts.ColumnLayout { + id: maincol + enabled: deviceCount + spacing: units.largeSpacing + + Layouts.RowLayout { + spacing: units.largeSpacing + + Controls.Label { + text: i18n("Device:") + } + + Controls.ComboBox { + id: deviceSelector + enabled: deviceCount > 1 + Layouts.Layout.fillWidth: true + model: deviceModel + textRole: "name" + + onCurrentIndexChanged: { + if (deviceCount) { + device = deviceModel[currentIndex] + if (!loading) { + changeSignal() + } + console.log("Configuration of device '" + + (currentIndex+1) + " : " + device.name + "' opened") + } + root.syncValuesFromBackend() + } + } + } + + Column { + spacing: units.smallSpacing * 2 + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.Label { + text: i18n("General settings:") + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.CheckBox { + id: deviceEnabled + text: i18n("Device enabled") + + function load() { + if (!maincol.enabled) { + checked = false + return + } + enabled = device.supportsDisableEvents + checked = enabled && device.enabled + } + + onCheckedChanged: { + if (enabled && !root.loading) { + device.enabled = checked + root.changeSignal() + } + } + + ToolTip { + text: i18n("Accept input through this device.") + } + } + + Controls.CheckBox { + id: leftHanded + text: i18n("Left handed mode") + + function load() { + if (!maincol.enabled) { + checked = false + return + } + enabled = device.supportsLeftHanded + checked = enabled && device.leftHanded + } + + onCheckedChanged: { + if (enabled && !root.loading) { + device.leftHanded = checked + root.changeSignal() + } + } + + ToolTip { + text: i18n("Swap left and right buttons.") + } + } + + Controls.CheckBox { + id: middleEmulation + text: i18n("Emulate middle button") + + function load() { + if (!maincol.enabled) { + checked = false + return + } + enabled = device.supportsMiddleEmulation + checked = enabled && device.middleEmulation + } + + onCheckedChanged: { + if (enabled && !root.loading) { + device.middleEmulation = checked + root.changeSignal() + } + } + + ToolTip { + text: i18n("Clicking left and right button simultaneously sends middle button click.") + } + } + } + } + } + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.Label { + text: i18n("Acceleration:") + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing * 2 + + Row { + Controls.Slider { + id: accelSpeed + anchors.verticalCenter: parent.verticalCenter + + tickmarksEnabled: true + + minimumValue: 1 + maximumValue: 10 + stepSize: 1 + + implicitWidth: units.gridUnit * 9 + + function load() { + enabled = device.supportsPointerAcceleration + if (!enabled) { + value = 0.1 + return + } + // transform libinput's pointer acceleration range [-1, 1] to slider range [1, 10] + value = 4.5 * device.pointerAcceleration + 5.5 + } + + onValueChanged: { + if (device != undefined && enabled && !root.loading) { + // transform slider range [1, 10] to libinput's pointer acceleration range [-1, 1] + device.pointerAcceleration = Math.round( (value - 5.5) / 4.5 * 100 ) / 100 + root.changeSignal() + } + } + } + } + + ExclGroupBox { + id: accelProfile + label: i18n("Acceleration Profile:") + model: [i18n("Flat"), i18n("Adaptive")] + + function load() { + enabled = device.supportsPointerAccelerationProfileAdaptive + + if (!enabled) { + itemAt(0).checked = false + itemAt(1).checked = false + return + } + + itemAt(0).tooltiptext = i18n("Cursor moves the same distance as finger.") + itemAt(1).tooltiptext = i18n("Cursor travel distance depends on movement speed of finger.") + + var toCheck = device.pointerAccelerationProfileAdaptive ? 1 : 0 + itemAt(toCheck).checked = true + } + + onCurrentChanged: { + if (enabled && !root.loading) { + device.pointerAccelerationProfileFlat = itemAt(0).checked + device.pointerAccelerationProfileAdaptive = itemAt(1).checked + root.changeSignal() + } + } + } + } + } + } + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.Label { + text: i18n("Scrolling:") + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + + Controls.CheckBox { + id: naturalScroll + text: i18n("Invert scroll direction") + + function load() { + enabled = device.supportsNaturalScroll + checked = enabled && device.naturalScroll + } + + onCheckedChanged: { + if (enabled && !root.loading) { + device.naturalScroll = checked + root.changeSignal() + } + } + + ToolTip { + text: i18n("Touchscreen like scrolling.") + } + } + } + } + } + } + } + } + } +} diff --git a/kcms/input/kcm/resources.qrc b/kcms/input/kcm/resources.qrc new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/resources.qrc @@ -0,0 +1,7 @@ + + + libinput/main.qml + libinput/components/ExclGroupBox.qml + libinput/components/ToolTip.qml + + diff --git a/kcms/input/kcmmouse.ui b/kcms/input/kcm/xlib/kcmmouse.ui rename from kcms/input/kcmmouse.ui rename to kcms/input/kcm/xlib/kcmmouse.ui diff --git a/kcms/input/kcm/xlib/xlib_config.h b/kcms/input/kcm/xlib/xlib_config.h new file mode 100644 --- /dev/null +++ b/kcms/input/kcm/xlib/xlib_config.h @@ -0,0 +1,74 @@ +/* + * Copyright 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * Copyright 1999 Dirk A. Mueller + * Copyright 2000 David Faure + * Copyright 2018 Roman Gilg + * + * 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 XLIBCONFIG_H +#define XLIBCONFIG_H + +#include "../configplugin.h" +#include "backends/x11/evdev_settings.h" + +#include "ui_kcmmouse.h" +#include + +#include + +class QCheckBox; +class QDoubleSpinBox; +class QSlider; +class QSpinBox; +class QTabWidget; + +class X11Backend; + +class XlibConfig : public ConfigPlugin, public Ui::KCMMouse +{ + Q_OBJECT +public: + XlibConfig(ConfigContainer *parent, InputBackend *backend); + ~XlibConfig() = default; + + static void kcmInit(); + + void load() override; + void save() override; + void defaults() override; + +private Q_SLOTS: + void slotHandedChanged(int val); + void slotScrollPolarityChanged(); + void checkAccess(); + void slotThreshChanged(int value); + void slotDragStartDistChanged(int value); + void slotWheelScrollLinesChanged(int value); + +private: + double getAccel(); + int getThreshold(); + Handed getHandedness(); + + void setAccel(double); + void setThreshold(int); + void setHandedness(Handed); + + X11Backend *m_backend; +}; + +#endif // XLIBCONFIG_H diff --git a/kcms/input/mouse.cpp b/kcms/input/kcm/xlib/xlib_config.cpp rename from kcms/input/mouse.cpp rename to kcms/input/kcm/xlib/xlib_config.cpp --- a/kcms/input/mouse.cpp +++ b/kcms/input/kcm/xlib/xlib_config.cpp @@ -1,214 +1,229 @@ /* - * mouse.cpp + * Copyright 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * Copyright 1999 Dirk A. Mueller + * Copyright 1999 Matthias Hoelzer-Kluepfel + * Copyright 2000 David Faure + * Copyright 2000 Bernd Gehrmann + * Copyright 2000 Rik Hemsley + * Copyright 2000 Brad Hughes + * Copyright 2001 Ralf Nolden + * Copyright 2004 Brad Hards + * Copyright 2018 Roman Gilg * - * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * 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. * - * Layout management, enhancements: - * Copyright (c) 1999 Dirk A. Mueller + * 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. * - * SC/DC/AutoSelect/ChangeCursor: - * Copyright (c) 2000 David Faure - * - * Double click interval, drag time & dist - * Copyright (c) 2000 Bernd Gehrmann - * - * Large cursor support - * Visual activation TODO: speed - * Copyright (c) 2000 Rik Hemsley - * - * White cursor support - * TODO: give user the option to choose a certain cursor font - * -> Theming - * - * General/Advanced tabs - * Copyright (c) 2000 Brad Hughes - * - * redesign for KDE 2.2 - * Copyright (c) 2001 Ralf Nolden - * - * Logitech mouse support - * Copyright (C) 2004 Brad Hards - * - * Requires the Qt widget libraries, available at no cost at - * http://www.troll.no/ - * - * 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. + * 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 "xlib_config.h" -#include -#include -#include -#include -#include -#include +#include "backends/x11/x11_backend.h" +#include "../configcontainer.h" + +#include "../../../migrationlib/kdelibs4config.h" + +#include +#include #include #include -#include +//#include #include #include #include #include -#include +#include +#include +#include +#include +#include +#include -#include "mouse.h" -#include "mousebackend.h" -#include "mousesettings.h" +#include -#include +#include +#include -#undef Below +void XlibConfig::kcmInit() +{ + X11Backend *backend = dynamic_cast(InputBackend::implementation()); + if (!backend) { + return; + } -#include "../migrationlib/kdelibs4config.h" + backend->settings()->load(backend); + backend->settings()->apply(backend, true); // force -K_PLUGIN_FACTORY(MouseConfigFactory, - registerPlugin(); // mouse - ) + KConfigGroup group = KConfig("kcminputrc", KConfig::NoGlobals).group("Mouse"); + QString theme = group.readEntry("cursorTheme", QString()); + QString size = group.readEntry("cursorSize", QString()); + if (backend) { + int intSize = -1; + if (size.isEmpty()) { + bool ok; + uint value = size.toUInt(&ok); + if (ok) { + intSize = value; + } + } + // Note: If you update this code, update kapplymousetheme as well. -MouseConfig::MouseConfig(QWidget *parent, const QVariantList &args) - : KCModule(parent, args), - backend(MouseBackend::implementation()) + // use a default value for theme only if it's not configured at all, not even in X resources + if (theme.isEmpty() && backend->currentCursorTheme().isEmpty()) { + theme = "breeze_cursors"; + } + backend->applyCursorTheme(theme, intSize); + } + + // Tell klauncher to set the XCURSOR_THEME and XCURSOR_SIZE environment + // variables when launching applications. + OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), + QStringLiteral("/KLauncher"), + QDBusConnection::sessionBus()); + if (!theme.isEmpty()) { + klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), theme); + } + if (!size.isEmpty()) { + klauncher.setLaunchEnv(QStringLiteral("XCURSOR_SIZE"), size); + } +} + +XlibConfig::XlibConfig(ConfigContainer *parent, InputBackend *backend) + : ConfigPlugin(parent), + m_backend(dynamic_cast(backend)) { setupUi(this); - handedGroup->setId(rightHanded, static_cast(MouseHanded::Right)); - handedGroup->setId(leftHanded, static_cast(MouseHanded::Left)); + handedGroup->setId(rightHanded, static_cast(Handed::Right)); + handedGroup->setId(leftHanded, static_cast(Handed::Left)); - connect(handedGroup, SIGNAL(buttonClicked(int)), this, SLOT(changed())); + connect(handedGroup, SIGNAL(buttonClicked(int)), m_parent, SLOT(changed())); connect(handedGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotHandedChanged(int))); - connect(doubleClick, SIGNAL(clicked()), SLOT(changed())); + connect(doubleClick, SIGNAL(clicked()), m_parent, SLOT(changed())); - connect(singleClick, SIGNAL(clicked()), this, SLOT(changed())); - connect(cbScrollPolarity, SIGNAL(clicked()), this, SLOT(changed())); + connect(singleClick, SIGNAL(clicked()), m_parent, SLOT(changed())); + connect(cbScrollPolarity, SIGNAL(clicked()), m_parent, SLOT(changed())); connect(cbScrollPolarity, SIGNAL(clicked()), this, SLOT(slotScrollPolarityChanged())); - connect(accel, SIGNAL(valueChanged(double)), this, SLOT(changed())); - connect(thresh, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(accel, SIGNAL(valueChanged(double)), m_parent, SLOT(changed())); + connect(thresh, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(thresh, SIGNAL(valueChanged(int)), this, SLOT(slotThreshChanged(int))); - connect(accelProfileComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); + connect(accelProfileComboBox, SIGNAL(currentIndexChanged(int)), m_parent, SLOT(changed())); slotThreshChanged(thresh->value()); // It would be nice if the user had a test field. // Selecting such values in milliseconds is not intuitive - connect(doubleClickInterval, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(doubleClickInterval, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); - connect(dragStartTime, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(dragStartTime, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); - connect(dragStartDist, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(dragStartDist, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(dragStartDist, SIGNAL(valueChanged(int)), this, SLOT(slotDragStartDistChanged(int))); slotDragStartDistChanged(dragStartDist->value()); - connect(wheelScrollLines, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(wheelScrollLines, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(wheelScrollLines, SIGNAL(valueChanged(int)), SLOT(slotWheelScrollLinesChanged(int))); slotWheelScrollLinesChanged(wheelScrollLines->value()); connect(mouseKeys, SIGNAL(clicked()), this, SLOT(checkAccess())); - connect(mouseKeys, SIGNAL(clicked()), this, SLOT(changed())); - connect(mk_delay, SIGNAL(valueChanged(int)), this, SLOT(changed())); - connect(mk_interval, SIGNAL(valueChanged(int)), this, SLOT(changed())); - connect(mk_time_to_max, SIGNAL(valueChanged(int)), this, SLOT(changed())); - connect(mk_max_speed, SIGNAL(valueChanged(int)), this, SLOT(changed())); - connect(mk_curve, SIGNAL(valueChanged(int)), this, SLOT(changed())); - - settings = new MouseSettings; + connect(mouseKeys, SIGNAL(clicked()), m_parent, SLOT(changed())); + connect(mk_delay, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); + connect(mk_interval, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); + connect(mk_time_to_max, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); + connect(mk_max_speed, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); + connect(mk_curve, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); KAboutData* about = new KAboutData(QStringLiteral("kcmmouse"), i18n("Mouse"), QStringLiteral("1.0"), QString(), KAboutLicense::GPL, - i18n("(c) 1997 - 2005 Mouse developers")); + i18n("(c) 1997 - 2018 Mouse developers")); about->addAuthor(i18n("Patrick Dowler")); about->addAuthor(i18n("Dirk A. Mueller")); about->addAuthor(i18n("David Faure")); about->addAuthor(i18n("Bernd Gehrmann")); about->addAuthor(i18n("Rik Hemsley")); about->addAuthor(i18n("Brad Hughes")); about->addAuthor(i18n("Ralf Nolden")); about->addAuthor(i18n("Brad Hards")); - setAboutData(about); + about->addAuthor(i18n("Roman Gilg")); + m_parent->setAboutData(about); } -void MouseConfig::checkAccess() +void XlibConfig::checkAccess() { mk_delay->setEnabled(mouseKeys->isChecked()); mk_interval->setEnabled(mouseKeys->isChecked()); mk_time_to_max->setEnabled(mouseKeys->isChecked()); mk_max_speed->setEnabled(mouseKeys->isChecked()); mk_curve->setEnabled(mouseKeys->isChecked()); } - -MouseConfig::~MouseConfig() -{ - delete settings; -} - -double MouseConfig::getAccel() +double XlibConfig::getAccel() { return accel->value(); } -void MouseConfig::setAccel(double val) +void XlibConfig::setAccel(double val) { accel->setValue(val); } -int MouseConfig::getThreshold() +int XlibConfig::getThreshold() { return thresh->value(); } -void MouseConfig::setThreshold(int val) +void XlibConfig::setThreshold(int val) { thresh->setValue(val); } - -MouseHanded MouseConfig::getHandedness() +Handed XlibConfig::getHandedness() { if (rightHanded->isChecked()) - return MouseHanded::Right; + return Handed::Right; else - return MouseHanded::Left; + return Handed::Left; } -void MouseConfig::setHandedness(MouseHanded val) +void XlibConfig::setHandedness(Handed val) { rightHanded->setChecked(false); leftHanded->setChecked(false); - if (val == MouseHanded::Right) { + if (val == Handed::Right) { rightHanded->setChecked(true); mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_rh.png")); } else { leftHanded->setChecked(true); mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_lh.png")); } - settings->handedNeedsApply = true; + m_backend->settings()->handedNeedsApply = true; } -void MouseConfig::load() + + + +void XlibConfig::load() { - KConfig config("kcminputrc"); - settings->load(&config, backend); + EvdevSettings *settings = m_backend->settings(); + + m_parent->kcmLoad(); + m_backend->load(); - // settings->load will trigger backend->load so information will be avaialbe - // here. // Only allow setting reversing scroll polarity if we have scroll buttons - if (backend) { - if (backend->supportScrollPolarity()) + if (m_backend) { + if (m_backend->supportScrollPolarity()) { cbScrollPolarity->setEnabled(true); cbScrollPolarity->show(); @@ -220,7 +235,7 @@ } } - auto accelerationProfiles = backend->supportedAccelerationProfiles(); + auto accelerationProfiles = m_backend->supportedAccelerationProfiles(); accelProfileComboBox->setEnabled(!accelerationProfiles.isEmpty()); accelProfileComboBox->setVisible(!accelerationProfiles.isEmpty()); accelerationProfileLabel->setEnabled(!accelerationProfiles.isEmpty()); @@ -235,7 +250,6 @@ idx++; } - rightHanded->setEnabled(settings->handedEnabled); leftHanded->setEnabled(settings->handedEnabled); if (cbScrollPolarity->isEnabled()) @@ -281,11 +295,13 @@ mk_curve->setValue(group.readEntry("MKCurve", 0)); checkAccess(); - emit changed(false); + emit m_parent->changed(false); } -void MouseConfig::save() +void XlibConfig::save() { + EvdevSettings *settings = m_backend->settings(); + settings->accelRate = getAccel(); settings->thresholdMove = getThreshold(); settings->handed = getHandedness(); @@ -298,9 +314,8 @@ settings->reverseScrollPolarity = cbScrollPolarity->isChecked(); settings->currentAccelProfile = accelProfileComboBox->itemData(accelProfileComboBox->currentIndex()).toString(); - settings->apply(backend); - KConfig config("kcminputrc"); - settings->save(&config); + m_backend->apply(); + settings->save(); KConfig ac("kaccessrc"); @@ -320,14 +335,14 @@ // restart kaccess KToolInvocation::startServiceByDesktopName("kaccess"); - emit changed(false); + emit m_parent->changed(false); } -void MouseConfig::defaults() +void XlibConfig::defaults() { setThreshold(2); setAccel(2); - setHandedness(MouseHanded::Right); + setHandedness(Handed::Right); cbScrollPolarity->setChecked(false); doubleClickInterval->setValue(400); dragStartTime->setValue(500); @@ -344,37 +359,39 @@ mk_curve->setValue(0); checkAccess(); - changed(); + m_parent->kcmDefaults(); + + m_parent->changed(true); } /** No descriptions */ -void MouseConfig::slotHandedChanged(int val) +void XlibConfig::slotHandedChanged(int val) { - if (val == static_cast(MouseHanded::Right)) + if (val == static_cast(Handed::Right)) mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_rh.png")); else mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_lh.png")); - settings->handedNeedsApply = true; + m_backend->settings()->handedNeedsApply = true; } -void MouseConfig::slotThreshChanged(int value) +void XlibConfig::slotThreshChanged(int value) { thresh->setSuffix(i18np(" pixel", " pixels", value)); } -void MouseConfig::slotDragStartDistChanged(int value) +void XlibConfig::slotDragStartDistChanged(int value) { dragStartDist->setSuffix(i18np(" pixel", " pixels", value)); } -void MouseConfig::slotWheelScrollLinesChanged(int value) +void XlibConfig::slotWheelScrollLinesChanged(int value) { wheelScrollLines->setSuffix(i18np(" line", " lines", value)); } -void MouseConfig::slotScrollPolarityChanged() +void XlibConfig::slotScrollPolarityChanged() { - settings->handedNeedsApply = true; + m_backend->settings()->handedNeedsApply = true; } -#include "mouse.moc" +#include "xlib_config.moc" diff --git a/kcms/input/main.cpp b/kcms/input/main.cpp deleted file mode 100644 --- a/kcms/input/main.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * main.cpp - * - * Copyright (c) 1999 Matthias Hoelzer-Kluepfel - * - * Requires the Qt widget libraries, available at no cost at - * http://www.troll.no/ - * - * 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 -#include -#include - -#include "mousesettings.h" -#include "mousebackend.h" - -#include - -extern "C" -{ - Q_DECL_EXPORT void kcminit_mouse() - { - KConfig* config = new KConfig("kcminputrc", KConfig::NoGlobals); - - auto backend = MouseBackend::implementation(); - - MouseSettings settings; - settings.load(config, backend); - settings.apply(backend, true); // force - - KConfigGroup group = config->group("Mouse"); - QString theme = group.readEntry("cursorTheme", QString()); - QString size = group.readEntry("cursorSize", QString()); - if (backend) { - int intSize = -1; - if (size.isEmpty()) { - bool ok; - uint value = size.toUInt(&ok); - if (ok) { - intSize = value; - } - } - // Note: If you update this code, update kapplymousetheme as well. - - // use a default value for theme only if it's not configured at all, not even in X resources - if (theme.isEmpty() && backend->currentCursorTheme().isEmpty()) { - theme = "breeze_cursors"; - } - backend->applyCursorTheme(theme, intSize); - } - - // Tell klauncher to set the XCURSOR_THEME and XCURSOR_SIZE environment - // variables when launching applications. - OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), - QStringLiteral("/KLauncher"), - QDBusConnection::sessionBus()); - if (!theme.isEmpty()) { - klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), theme); - } - if (!size.isEmpty()) { - klauncher.setLaunchEnv(QStringLiteral("XCURSOR_SIZE"), size); - } - - delete config; - } -} diff --git a/kcms/input/misc/CMakeLists.txt b/kcms/input/misc/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/kcms/input/misc/CMakeLists.txt @@ -0,0 +1,6 @@ +install( FILES mouse_rh.png mouse_lh.png + DESTINATION ${KDE_INSTALL_DATADIR}/kcminput/pics +) +install( FILES cursor_large_black.pcf.gz cursor_large_white.pcf.gz cursor_small_white.pcf.gz + DESTINATION ${KDE_INSTALL_DATADIR}/kcminput +) diff --git a/kcms/input/consoleUserPerms b/kcms/input/misc/consoleUserPerms rename from kcms/input/consoleUserPerms rename to kcms/input/misc/consoleUserPerms diff --git a/kcms/input/cursor_large.bdf b/kcms/input/misc/cursor_large.bdf rename from kcms/input/cursor_large.bdf rename to kcms/input/misc/cursor_large.bdf diff --git a/kcms/input/cursor_large_black.pcf.gz b/kcms/input/misc/cursor_large_black.pcf.gz rename from kcms/input/cursor_large_black.pcf.gz rename to kcms/input/misc/cursor_large_black.pcf.gz index c9b458390cf7326545ccb6b8cdf25c5c4de45f03..c9b458390cf7326545ccb6b8cdf25c5c4de45f03 GIT binary patch literal 3339 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 literal 3521 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 diff --git a/kcms/input/cursor_small_white.pcf.gz b/kcms/input/misc/cursor_small_white.pcf.gz rename from kcms/input/cursor_small_white.pcf.gz rename to kcms/input/misc/cursor_small_white.pcf.gz index 618905ea70bbcb363d84aad2597357aa2da802c4..618905ea70bbcb363d84aad2597357aa2da802c4 GIT binary patch literal 3524 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 literal 3524 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 diff --git a/kcms/input/pics/mouse_lh.png b/kcms/input/misc/mouse_lh.png rename from kcms/input/pics/mouse_lh.png rename to kcms/input/misc/mouse_lh.png index 796d5994252bad45f8f076f1cb1e3621efd9cc4f..796d5994252bad45f8f076f1cb1e3621efd9cc4f GIT binary patch literal 10704 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 literal 10793 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 diff --git a/kcms/input/pics/mouse_rh.svgz b/kcms/input/misc/mouse_rh.svgz rename from kcms/input/pics/mouse_rh.svgz rename to kcms/input/misc/mouse_rh.svgz index fc2b069a8d842c03af188c03d808f3dfb621ce93..fc2b069a8d842c03af188c03d808f3dfb621ce93 GIT binary patch literal 4528 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 O;QtM-4PW0)MgRa0Bl0l- literal 4528 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 O;QtM-4PW0)MgRa0Bl0l- diff --git a/kcms/input/mouse.h b/kcms/input/mouse.h deleted file mode 100644 --- a/kcms/input/mouse.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * mouse.h - * - * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca - * - * Layout management, enhancements: - * Copyright (c) 1999 Dirk A. Mueller - * - * SC/DC/AutoSelect/ChangeCursor: - * Copyright (c) 2000 David Faure - * - * Requires the Qt widget libraries, available at no cost at - * http://www.troll.no/ - * - * 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 __MOUSECONFIG_H__ -#define __MOUSECONFIG_H__ - -#include - -#include -#include "ui_kcmmouse.h" -#include "mousesettings.h" - -class QCheckBox; -class QDoubleSpinBox; -class QSlider; -class QSpinBox; -class QTabWidget; - -class MouseSettings; -class MouseBackend; - -class MouseConfig : public KCModule, public Ui::KCMMouse -{ - Q_OBJECT -public: - MouseConfig(QWidget *parent, const QVariantList &args); - ~MouseConfig(); - - void save(); - void load(); - void defaults(); - -private Q_SLOTS: - void slotHandedChanged(int val); - void slotScrollPolarityChanged(); - void checkAccess(); - void slotThreshChanged(int value); - void slotDragStartDistChanged(int value); - void slotWheelScrollLinesChanged(int value); - -private: - double getAccel(); - int getThreshold(); - MouseHanded getHandedness(); - - void setAccel(double); - void setThreshold(int); - void setHandedness(MouseHanded); - - MouseSettings *settings; - - MouseBackend *backend; -}; - -#endif diff --git a/kcms/input/mousebackend.h b/kcms/input/mousebackend.h deleted file mode 100644 --- a/kcms/input/mousebackend.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2017 Xuetian Weng - * - * 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 MOUSEBACKEND_H -#define MOUSEBACKEND_H - -#include -#include "mousesettings.h" - -class MouseBackend : public QObject -{ - Q_OBJECT -protected: - explicit MouseBackend(QObject *parent) : QObject(parent) {} - -public: - static MouseBackend *implementation(); - - virtual bool isValid() const = 0; - - // 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; - - // 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; - - virtual QString currentCursorTheme() = 0; - virtual void applyCursorTheme(const QString &name, int size) = 0; -}; - -#endif // MOUSEBACKEND_H diff --git a/kcms/input/pics/CMakeLists.txt b/kcms/input/pics/CMakeLists.txt deleted file mode 100644 --- a/kcms/input/pics/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -install( FILES mouse_rh.png mouse_lh.png DESTINATION ${KDE_INSTALL_DATADIR}/kcminput/pics ) diff --git a/kcms/input/logging.cpp b/kcms/input/plugin.h rename from kcms/input/logging.cpp rename to kcms/input/plugin.h --- a/kcms/input/logging.cpp +++ b/kcms/input/plugin.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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 @@ -15,7 +15,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef PLUGIN_H +#define PLUGIN_H -#include "logging.h" +#include -Q_LOGGING_CATEGORY(KCM_INPUT, "kcm_input") +K_PLUGIN_FACTORY_DECLARATION(MousePluginFactory) + +#endif // PLUGIN_H diff --git a/kcms/input/logging.h b/kcms/input/plugin.cpp rename from kcms/input/logging.h rename to kcms/input/plugin.cpp --- a/kcms/input/logging.h +++ b/kcms/input/plugin.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2017 Xuetian Weng + * Copyright 2018 Roman Gilg * * 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 @@ -15,11 +15,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "kcm/configcontainer.h" -#ifndef KCM_INPUT_LOGGING_H -#define KCM_INPUT_LOGGING_H +#include -#include +K_PLUGIN_FACTORY(MousePluginFactory, + registerPlugin(); +) -Q_DECLARE_LOGGING_CATEGORY(KCM_INPUT) -#endif +#include