diff --git a/kcms/CMakeLists.txt b/kcms/CMakeLists.txt --- a/kcms/CMakeLists.txt +++ b/kcms/CMakeLists.txt @@ -58,7 +58,7 @@ add_subdirectory(cursortheme) if (SYNAPTICS_FOUND AND X11_Xinput_FOUND) - add_subdirectory(touchpad) + add_subdirectory(touchpadx) endif() if(FONTCONFIG_FOUND AND FREETYPE_FOUND) diff --git a/kcms/touchpad/CMakeLists.txt b/kcms/touchpad/CMakeLists.txt --- a/kcms/touchpad/CMakeLists.txt +++ b/kcms/touchpad/CMakeLists.txt @@ -1,6 +1,6 @@ find_package(KDED ${KF5_MIN_VERSION} CONFIG REQUIRED) -add_definitions(-DTRANSLATION_DOMAIN=\"kcm_touchpad\") +add_definitions(-DTRANSLATION_DOMAIN=\"kcm_touchpadx\") set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules diff --git a/kcms/touchpad/Messages.sh b/kcms/touchpad/Messages.sh --- a/kcms/touchpad/Messages.sh +++ b/kcms/touchpad/Messages.sh @@ -1,6 +1,6 @@ #!/bin/sh $EXTRACTRC `find src -path src/applet -prune -o \( -name \*.rc -o -name \*.ui -o -name \*.kcfg \) -print` >> rc.cpp -$XGETTEXT rc.cpp `find src -path src/applet -prune -o \( -name \*.cpp -o -name \*.h \) -print` -o $podir/kcm_touchpad.pot +$XGETTEXT rc.cpp `find src -path src/applet -prune -o \( -name \*.cpp -o -name \*.h \) -print` -o $podir/kcm_touchpadx.pot $XGETTEXT `find src/applet -name \*.qml -o -name \*.js` -o $podir/plasma_applet_touchpad.pot diff --git a/kcms/touchpad/README.md b/kcms/touchpad/README.md --- a/kcms/touchpad/README.md +++ b/kcms/touchpad/README.md @@ -1,10 +1,10 @@ -Touchpad KCM +TouchpadX KCM ============ -* KCModule (Wayland + X) -* Daemon (X only) +* KCModule +* Daemon - Automatically enable/disable touchpad when typing and/or when mouse is plugged in - Enable/disable touchpad with keyboard shortcuts -* Applet (X only) +* Applet - Shows touchpad's state - Toggle touchpad with single click diff --git a/kcms/touchpad/src/CMakeLists.txt b/kcms/touchpad/src/CMakeLists.txt --- a/kcms/touchpad/src/CMakeLists.txt +++ b/kcms/touchpad/src/CMakeLists.txt @@ -1,5 +1,5 @@ # KI18N Translation Domain for this library -add_definitions(-DTRANSLATION_DOMAIN=\"kcm_touchpad\") +add_definitions(-DTRANSLATION_DOMAIN=\"kcm_touchpadx\") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/version.h" @@ -12,13 +12,11 @@ ) include(backends/x11.cmake) -include(backends/kwin_wayland.cmake) set(SRCS ${SRCS} kcm/touchpadconfigcontainer.cpp kcm/touchpadconfigplugin.cpp - kcm/libinput/touchpadconfiglibinput.cpp kcm/xlib/customslider.cpp kcm/xlib/sliderpair.cpp kcm/xlib/testarea.cpp @@ -32,8 +30,6 @@ ${CMAKE_CURRENT_BINARY_DIR}/org.kde.touchpad.xml ) -qt5_add_resources( SRCS kcm/resources.qrc ) - kconfig_add_kcfg_files(SRCS kcm/xlib/touchpadparameters.kcfgc) ki18n_wrap_ui(SRCS @@ -83,7 +79,7 @@ add_subdirectory(applet) -install(FILES kcm/kcm_touchpad.desktop +install(FILES kcm/kcm_touchpadx.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/kcms/touchpad/src/applet/CMakeLists.txt b/kcms/touchpad/src/applet/CMakeLists.txt --- a/kcms/touchpad/src/applet/CMakeLists.txt +++ b/kcms/touchpad/src/applet/CMakeLists.txt @@ -1,4 +1,4 @@ -remove_definitions(-DTRANSLATION_DOMAIN="kcm_touchpad") +remove_definitions(-DTRANSLATION_DOMAIN="kcm_touchpadx") add_definitions(-DTRANSLATION_DOMAIN="plasma_applet_touchpad") plasma_install_package(qml touchpad) diff --git a/kcms/touchpad/src/applet/qml/metadata.desktop b/kcms/touchpad/src/applet/qml/metadata.desktop --- a/kcms/touchpad/src/applet/qml/metadata.desktop +++ b/kcms/touchpad/src/applet/qml/metadata.desktop @@ -136,6 +136,6 @@ X-KDE-PluginInfo-Website=https://projects.kde.org/projects/playground/utils/kcm-touchpad/ X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript -X-Plasma-ConfigPlugins=kcm_touchpad +X-Plasma-ConfigPlugins=kcm_touchpadx X-Plasma-MainScript=ui/touchpad.qml X-Plasma-NotificationArea=true diff --git a/kcms/touchpad/src/backends/kwin_wayland.cmake b/kcms/touchpad/src/backends/kwin_wayland.cmake deleted file mode 100644 --- a/kcms/touchpad/src/backends/kwin_wayland.cmake +++ /dev/null @@ -1,5 +0,0 @@ -SET(backend_SRCS - ${backend_SRCS} - backends/kwin_wayland/kwinwaylandbackend.cpp - backends/kwin_wayland/kwinwaylandtouchpad.cpp -) diff --git a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandbackend.h b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandbackend.h deleted file mode 100644 --- a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandbackend.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -#ifndef KWINWAYLANDBACKEND_H -#define KWINWAYLANDBACKEND_H - -#include "touchpadbackend.h" - -#include - -class KWinWaylandTouchpad; -class QDBusInterface; - -class KWinWaylandBackend : public TouchpadBackend -{ - Q_OBJECT - - Q_PROPERTY(int touchpadCount READ touchpadCount 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 touchpadCount() 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 findTouchpads(); - - QDBusInterface* m_deviceManager; - QVector m_devices; - - QString m_errorString = QString(); -}; - -#endif // KWINWAYLANDBACKEND_H diff --git a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandbackend.cpp b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandbackend.cpp deleted file mode 100644 --- a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandbackend.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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. - */ - -#include "kwinwaylandbackend.h" -#include "kwinwaylandtouchpad.h" - -#include - -#include - -#include -#include -#include -#include - -#include "logging.h" - -KWinWaylandBackend::KWinWaylandBackend(QObject *parent) : - TouchpadBackend(parent) -{ - - m_deviceManager = new QDBusInterface (QStringLiteral("org.kde.KWin"), - QStringLiteral("/org/kde/KWin/InputDevice"), - QStringLiteral("org.kde.KWin.InputDeviceManager"), - QDBusConnection::sessionBus(), - this); - - findTouchpads(); - - 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::findTouchpads() -{ - QStringList devicesSysNames; - const QVariant reply = m_deviceManager->property("devicesSysNames"); - if (reply.isValid()) { - qCDebug(KCM_TOUCHPAD) << "Devices list received successfully from KWin."; - devicesSysNames = reply.toStringList(); - } - else { - qCCritical(KCM_TOUCHPAD) << "Error on receiving device list from KWin."; - m_errorString = i18n("Querying input devices failed. Please reopen this settings module."); - return; - } - - bool touchpadFound = false; - for (QString sn : devicesSysNames) { - QDBusInterface deviceIface(QStringLiteral("org.kde.KWin"), - QStringLiteral("/org/kde/KWin/InputDevice/") + sn, - QStringLiteral("org.kde.KWin.InputDevice"), - QDBusConnection::sessionBus(), - this); - const QVariant reply = deviceIface.property("touchpad"); - if (reply.isValid() && reply.toBool()) { - KWinWaylandTouchpad* tp = new KWinWaylandTouchpad(sn); - if (!tp->init()) { - qCCritical(KCM_TOUCHPAD) << "Error on creating touchpad object" << sn; - m_errorString = i18n("Critical error on reading fundamental device infos for touchpad %1.", sn); - return; - } - m_devices.append(tp); - touchpadFound = true; - qCDebug(KCM_TOUCHPAD).nospace() << "Touchpad found: " << tp->name() << " (" << tp->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("touchpad"); - if (reply.isValid() && reply.toBool()) { - KWinWaylandTouchpad* tp = new KWinWaylandTouchpad(sysName); - if (!tp->init() || !tp->getConfig()) { - emit touchpadAdded(false); - return; - } - m_devices.append(tp); - qCDebug(KCM_TOUCHPAD).nospace() << "Touchpad connected: " << tp->name() << " (" << tp->sysName() << ")"; - emit touchpadAdded(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; - } - - KWinWaylandTouchpad *tp = static_cast(*it); - qCDebug(KCM_TOUCHPAD).nospace() << "Touchpad disconnected: " << tp->name() << " (" << tp->sysName() << ")"; - - int index = it - m_devices.cbegin(); - m_devices.removeAt(index); - emit touchpadRemoved(index); -} diff --git a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.h b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.h deleted file mode 100644 --- a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.h +++ /dev/null @@ -1,458 +0,0 @@ -/* - * 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. - */ - -#ifndef KWINWAYLANDTOUCHPAD_H -#define KWINWAYLANDTOUCHPAD_H - -#include -#include - -class QDBusInterface; - -class KWinWaylandTouchpad : 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 supportsDisableEventsOnExternalMouse READ supportsDisableEventsOnExternalMouse CONSTANT) - - Q_PROPERTY(bool supportsDisableWhileTyping READ supportsDisableWhileTyping CONSTANT) - Q_PROPERTY(bool disableWhileTypingEnabledByDefault READ disableWhileTypingEnabledByDefault CONSTANT) - Q_PROPERTY(bool disableWhileTyping READ isDisableWhileTyping WRITE setDisableWhileTyping NOTIFY disableWhileTypingChanged) - - 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) - - // - // tapping - Q_PROPERTY(int tapFingerCount READ tapFingerCount CONSTANT) - Q_PROPERTY(bool tapToClickEnabledByDefault READ tapToClickEnabledByDefault CONSTANT) - Q_PROPERTY(bool tapToClick READ isTapToClick WRITE setTapToClick NOTIFY tapToClickChanged) - - Q_PROPERTY(bool supportsLmrTapButtonMap READ supportsLmrTapButtonMap CONSTANT) - Q_PROPERTY(bool lmrTapButtonMapEnabledByDefault READ lmrTapButtonMapEnabledByDefault CONSTANT) - Q_PROPERTY(bool lmrTapButtonMap READ lmrTapButtonMap WRITE setLmrTapButtonMap NOTIFY lmrTapButtonMapChanged) - - Q_PROPERTY(bool tapAndDragEnabledByDefault READ tapAndDragEnabledByDefault CONSTANT) - Q_PROPERTY(bool tapAndDrag READ isTapAndDrag WRITE setTapAndDrag NOTIFY tapAndDragChanged) - - Q_PROPERTY(bool tapDragLockEnabledByDefault READ tapDragLockEnabledByDefault CONSTANT) - Q_PROPERTY(bool tapDragLock READ isTapDragLock WRITE setTapDragLock NOTIFY tapDragLockChanged) - - // - // 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) - - Q_PROPERTY(bool supportsScrollTwoFinger READ supportsScrollTwoFinger CONSTANT) - Q_PROPERTY(bool scrollTwoFingerEnabledByDefault READ scrollTwoFingerEnabledByDefault CONSTANT) - Q_PROPERTY(bool scrollTwoFinger READ isScrollTwoFinger WRITE setScrollTwoFinger NOTIFY scrollMethodChanged) - - Q_PROPERTY(bool supportsScrollEdge READ supportsScrollEdge CONSTANT) - Q_PROPERTY(bool scrollEdgeEnabledByDefault READ scrollEdgeEnabledByDefault CONSTANT) - Q_PROPERTY(bool scrollEdge READ isScrollEdge WRITE setScrollEdge NOTIFY scrollMethodChanged) - - Q_PROPERTY(bool supportsScrollOnButtonDown READ supportsScrollOnButtonDown CONSTANT) - Q_PROPERTY(bool scrollOnButtonDownEnabledByDefault READ scrollOnButtonDownEnabledByDefault CONSTANT) - Q_PROPERTY(bool scrollOnButtonDown READ isScrollOnButtonDown WRITE setScrollOnButtonDown NOTIFY scrollMethodChanged) - - Q_PROPERTY(quint32 defaultScrollButton READ defaultScrollButton CONSTANT) - Q_PROPERTY(quint32 scrollButton READ scrollButton WRITE setScrollButton NOTIFY scrollButtonChanged) - -public: - KWinWaylandTouchpad(QString dbusName); - ~KWinWaylandTouchpad() 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 supportsDisableEventsOnExternalMouse() const { - return m_supportsDisableEventsOnExternalMouse.val; - } - - bool supportsDisableWhileTyping() const { - return m_supportsDisableWhileTyping.val; - } - bool disableWhileTypingEnabledByDefault() const { - return m_disableWhileTypingEnabledByDefault.val; - } - bool isDisableWhileTyping() const { - return m_disableWhileTyping.val; - } - void setDisableWhileTyping(bool set) { - m_disableWhileTyping.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); - } - - // - // tapping - int tapFingerCount() const { - return m_tapFingerCount.val; - } - bool tapToClickEnabledByDefault() const { - return m_tapToClickEnabledByDefault.val; - } - bool isTapToClick() const { - return m_tapToClick.val; - } - void setTapToClick(bool set) { - m_tapToClick.set(set); - } - - bool supportsLmrTapButtonMap() const { - return m_tapFingerCount.val > 1; - } - bool lmrTapButtonMapEnabledByDefault() const { - return m_lmrTapButtonMapEnabledByDefault.val; - } - bool lmrTapButtonMap() const { - return m_lmrTapButtonMap.val; - } - void setLmrTapButtonMap(bool set) { - m_lmrTapButtonMap.set(set); - } - - bool tapAndDragEnabledByDefault() const { - return m_tapAndDragEnabledByDefault.val; - } - bool isTapAndDrag() const { - return m_tapAndDrag.val; - } - void setTapAndDrag(bool set) { - m_tapAndDrag.set(set); - } - - bool tapDragLockEnabledByDefault() const { - return m_tapDragLockEnabledByDefault.val; - } - bool isTapDragLock() const { - return m_tapDragLock.val; - } - void setTapDragLock(bool set) { - m_tapDragLock.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); - } - - bool supportsScrollTwoFinger() const { - return m_supportsScrollTwoFinger.val; - } - bool scrollTwoFingerEnabledByDefault() const { - return m_scrollTwoFingerEnabledByDefault.val; - } - bool isScrollTwoFinger() const { - return m_isScrollTwoFinger.val; - } - void setScrollTwoFinger(bool set) { - m_isScrollTwoFinger.set(set); - } - - bool supportsScrollEdge() const { - return m_supportsScrollEdge.val; - } - bool scrollEdgeEnabledByDefault() const { - return m_scrollEdgeEnabledByDefault.val; - } - bool isScrollEdge() const { - return m_isScrollEdge.val; - } - void setScrollEdge(bool set) { - m_isScrollEdge.set(set); - } - - bool supportsScrollOnButtonDown() const { - return m_supportsScrollOnButtonDown.val; - } - bool scrollOnButtonDownEnabledByDefault() const { - return m_scrollOnButtonDownEnabledByDefault.val; - } - bool isScrollOnButtonDown() const { - return m_isScrollOnButtonDown.val; - } - void setScrollOnButtonDown(bool set) { - m_isScrollOnButtonDown.set(set); - } - - quint32 defaultScrollButton() const { - return m_defaultScrollButton.val; - } - quint32 scrollButton() const { - return m_scrollButton.val; - } - void setScrollButton(quint32 button) { - m_scrollButton.set(button); - } - -Q_SIGNALS: - void leftHandedChanged(); - void pointerAccelerationChanged(); - void pointerAccelerationProfileChanged(); - void enabledChanged(); - void tapToClickChanged(); - void tapAndDragChanged(); - void tapDragLockChanged(); - void lmrTapButtonMapChanged(); - void disableWhileTypingChanged(); - void middleEmulationChanged(); - void naturalScrollChanged(); - void scrollMethodChanged(); - void scrollButtonChanged(); - -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_supportsDisableEventsOnExternalMouse = Prop("supportsDisableEventsOnExternalMouse"); - - Prop m_supportsDisableWhileTyping = Prop("supportsDisableWhileTyping"); - Prop m_disableWhileTypingEnabledByDefault = Prop("disableWhileTypingEnabledByDefault"); - Prop m_disableWhileTyping = Prop("disableWhileTyping"); - - 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"); - - // - // tapping - Prop m_tapFingerCount = Prop("tapFingerCount"); - Prop m_tapToClickEnabledByDefault = Prop("tapToClickEnabledByDefault"); - Prop m_tapToClick = Prop("tapToClick"); - - Prop m_lmrTapButtonMapEnabledByDefault = Prop("lmrTapButtonMapEnabledByDefault"); - Prop m_lmrTapButtonMap = Prop("lmrTapButtonMap"); - - Prop m_tapAndDragEnabledByDefault = Prop("tapAndDragEnabledByDefault"); - Prop m_tapAndDrag = Prop("tapAndDrag"); - Prop m_tapDragLockEnabledByDefault = Prop("tapDragLockEnabledByDefault"); - Prop m_tapDragLock = Prop("tapDragLock"); - - // - // scrolling - Prop m_supportsNaturalScroll = Prop("supportsNaturalScroll"); - Prop m_naturalScrollEnabledByDefault = Prop("naturalScrollEnabledByDefault"); - Prop m_naturalScroll = Prop("naturalScroll"); - - Prop m_supportsScrollTwoFinger = Prop("supportsScrollTwoFinger"); - Prop m_scrollTwoFingerEnabledByDefault = Prop("scrollTwoFingerEnabledByDefault"); - Prop m_isScrollTwoFinger = Prop("scrollTwoFinger"); - - Prop m_supportsScrollEdge = Prop("supportsScrollEdge"); - Prop m_scrollEdgeEnabledByDefault = Prop("scrollEdgeEnabledByDefault"); - Prop m_isScrollEdge = Prop("scrollEdge"); - - Prop m_supportsScrollOnButtonDown = Prop("supportsScrollOnButtonDown"); - Prop m_scrollOnButtonDownEnabledByDefault = Prop("scrollOnButtonDownEnabledByDefault"); - Prop m_isScrollOnButtonDown = Prop("scrollOnButtonDown"); - - Prop m_defaultScrollButton = Prop("defaultScrollButton"); - Prop m_scrollButton = Prop("scrollButton"); - - - QDBusInterface *m_iface; -}; - -#endif diff --git a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.cpp b/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.cpp deleted file mode 100644 --- a/kcms/touchpad/src/backends/kwin_wayland/kwinwaylandtouchpad.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* - * 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. - */ - -#include "kwinwaylandtouchpad.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()); } -} - -KWinWaylandTouchpad::KWinWaylandTouchpad(QString dbusName) -{ - m_iface = new QDBusInterface(QStringLiteral("org.kde.KWin"), - QStringLiteral("/org/kde/KWin/InputDevice/") + dbusName, - QStringLiteral("org.kde.KWin.InputDevice"), - QDBusConnection::sessionBus(), - this); -} - -KWinWaylandTouchpad::~KWinWaylandTouchpad() -{ - delete m_iface; -} - -bool KWinWaylandTouchpad::init() -{ - // need to do it here in order to populate combobox and handle events - return valueLoader(m_name) && valueLoader(m_sysName); -} - -bool KWinWaylandTouchpad::getConfig() -{ - bool success = true; - - // general - success &= valueLoader(m_supportsDisableEvents); - success &= valueLoader(m_supportsLeftHanded); - success &= valueLoader(m_supportedButtons); - success &= valueLoader(m_leftHandedEnabledByDefault); - success &= valueLoader(m_enabled); - success &= valueLoader(m_leftHanded); - // advanced - success &= valueLoader(m_supportsPointerAcceleration); - success &= valueLoader(m_supportsPointerAccelerationProfileFlat); - success &= valueLoader(m_supportsPointerAccelerationProfileAdaptive); - success &= valueLoader(m_supportsDisableWhileTyping); - success &= valueLoader(m_supportsDisableEventsOnExternalMouse); - success &= valueLoader(m_defaultPointerAcceleration); - success &= valueLoader(m_defaultPointerAccelerationProfileFlat); - success &= valueLoader(m_defaultPointerAccelerationProfileAdaptive); - success &= valueLoader(m_disableWhileTypingEnabledByDefault); - success &= valueLoader(m_leftHandedEnabledByDefault); - success &= valueLoader(m_pointerAcceleration); - success &= valueLoader(m_pointerAccelerationProfileFlat); - success &= valueLoader(m_pointerAccelerationProfileAdaptive); - success &= valueLoader(m_disableWhileTyping); - // tapping - success &= valueLoader(m_tapFingerCount); - success &= valueLoader(m_supportsMiddleEmulation); - success &= valueLoader(m_tapToClickEnabledByDefault); - success &= valueLoader(m_tapAndDragEnabledByDefault); - success &= valueLoader(m_tapDragLockEnabledByDefault); - success &= valueLoader(m_middleEmulationEnabledByDefault); - success &= valueLoader(m_tapToClick); - success &= valueLoader(m_tapAndDrag); - success &= valueLoader(m_tapDragLock); - success &= valueLoader(m_middleEmulation); - success &= valueLoader(m_lmrTapButtonMapEnabledByDefault); - success &= valueLoader(m_lmrTapButtonMap); - // scrolling modes avail - success &= valueLoader(m_supportsNaturalScroll); - success &= valueLoader(m_supportsScrollTwoFinger); - success &= valueLoader(m_supportsScrollEdge); - success &= valueLoader(m_supportsScrollOnButtonDown); - // default scrolling modes - success &= valueLoader(m_naturalScrollEnabledByDefault); - success &= valueLoader(m_scrollTwoFingerEnabledByDefault); - success &= valueLoader(m_scrollEdgeEnabledByDefault); - success &= valueLoader(m_scrollOnButtonDownEnabledByDefault); - success &= valueLoader(m_defaultScrollButton); - // current scrolling mode - success &= valueLoader(m_naturalScroll); - success &= valueLoader(m_isScrollTwoFinger); - success &= valueLoader(m_isScrollEdge); - success &= valueLoader(m_isScrollOnButtonDown); - success &= valueLoader(m_scrollButton); - - return success; -} - -bool KWinWaylandTouchpad::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_disableWhileTyping.set(m_disableWhileTypingEnabledByDefault); - - m_tapToClick.set(m_tapToClickEnabledByDefault); - m_tapAndDrag.set(m_tapAndDragEnabledByDefault); - m_tapDragLock.set(m_tapDragLockEnabledByDefault); - m_middleEmulation.set(m_middleEmulationEnabledByDefault); - - m_naturalScroll.set(m_naturalScrollEnabledByDefault); - m_isScrollTwoFinger.set(m_scrollTwoFingerEnabledByDefault); - m_isScrollEdge.set(m_scrollEdgeEnabledByDefault); - m_isScrollOnButtonDown.set(m_scrollOnButtonDownEnabledByDefault); - - return true; -} - -bool KWinWaylandTouchpad::applyConfig() -{ - QVector msgs; - - msgs << valueWriter(m_enabled) - << valueWriter(m_leftHanded) - << valueWriter(m_pointerAcceleration) - << valueWriter(m_defaultPointerAccelerationProfileFlat) - << valueWriter(m_defaultPointerAccelerationProfileAdaptive) - - << valueWriter(m_disableWhileTyping) - << valueWriter(m_middleEmulation) - - << valueWriter(m_tapToClick) - << valueWriter(m_tapAndDrag) - << valueWriter(m_tapDragLock) - << valueWriter(m_lmrTapButtonMap) - - << valueWriter(m_naturalScroll) - << valueWriter(m_isScrollTwoFinger) - << valueWriter(m_isScrollEdge) - << valueWriter(m_isScrollOnButtonDown) - << valueWriter(m_scrollButton); - - bool success = true; - QString error_msg; - - for (QString m : msgs) { - if (!m.isNull()) { - qCCritical(KCM_TOUCHPAD) << "in error:" << m; - if (!success) { - error_msg.append("\n"); - } - error_msg.append(m); - success = false; - } - } - - if (!success) { - qCCritical(KCM_TOUCHPAD) << error_msg; - } - return success; -} - -bool KWinWaylandTouchpad::isChangedConfig() const -{ - return m_enabled.changed() || - m_leftHanded.changed() || - m_pointerAcceleration.changed() || - m_pointerAccelerationProfileFlat.changed() || - m_pointerAccelerationProfileAdaptive.changed() || - m_disableWhileTyping.changed() || - m_middleEmulation.changed() || - m_tapToClick.changed() || - m_tapAndDrag.changed() || - m_tapDragLock.changed() || - m_lmrTapButtonMap.changed() || - m_naturalScroll.changed() || - m_isScrollTwoFinger.changed() || - m_isScrollEdge.changed() || - m_isScrollOnButtonDown.changed() || - m_scrollButton.changed(); -} - -template -QString KWinWaylandTouchpad::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_TOUCHPAD) << error.message(); - return error.message(); - } - return QString(); -} - -template -bool KWinWaylandTouchpad::valueLoader(Prop &prop) -{ - QVariant reply = m_iface->property(prop.dbus); - if (!reply.isValid()) { - qCCritical(KCM_TOUCHPAD) << "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/touchpad/src/kcm/libinput/main.qml b/kcms/touchpad/src/kcm/libinput/main.qml deleted file mode 100644 --- a/kcms/touchpad/src/kcm/libinput/main.qml +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright 2017 Roman Gilg - * Copyright 2018 Furkan Tokac - * - * 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 2.0 as Controls -import QtQuick.Layouts 1.3 as Layouts - -import org.kde.kcm 1.1 as KCM -import org.kde.kirigami 2.4 as Kirigami - -// TODO: Change ScrollablePage as KCM.SimpleKCM -// after rewrite the KCM in KConfigModule. -Kirigami.ScrollablePage { - id: root - - spacing: Kirigami.Units.smallSpacing - - property size minimumSizeHint: Qt.size(formLayout.width/2, deviceSelector.height) - - property alias deviceIndex: deviceSelector.currentIndex - signal changeSignal() - - property QtObject touchpad - property int touchpadCount: backend.touchpadCount - - property bool loading: false - - function resetModel(index) { - touchpadCount = backend.touchpadCount - formLayout.enabled = touchpadCount - deviceSelector.enabled = touchpadCount > 1 - - loading = true - if (touchpadCount) { - touchpad = deviceModel[index] - deviceSelector.model = deviceModel - deviceSelector.currentIndex = index - console.log("Touchpad configuration of device '" + - (index + 1) + " : " + touchpad.name + "' opened") - } else { - deviceSelector.model = [""] - console.log("No touchpad found") - } - loading = false - } - - function syncValuesFromBackend() { - loading = true - - deviceEnabled.load() - dwt.load() - leftHanded.load() - accelSpeed.load() - accelProfile.load() - tapToClick.load() - tapAndDrag.load() - tapAndDragLock.load() - multiTap.load() - scrollMethod.load() - naturalScroll.load() - - loading = false - } - - Kirigami.FormLayout { - id: formLayout - - // Device - Controls.ComboBox { - Kirigami.FormData.label: i18n("Device:") - id: deviceSelector - - enabled: touchpadCount > 1 - Layouts.Layout.fillWidth: true - model: deviceModel - textRole: "name" - - onCurrentIndexChanged: { - if (touchpadCount) { - touchpad = deviceModel[currentIndex] - if (!loading) { - changeSignal() - } - console.log("Touchpad configuration of device '" + - (currentIndex+1) + " : " + touchpad.name + "' opened") - } - root.syncValuesFromBackend() - } - } - - Kirigami.Separator { - } - - // General settings - Controls.CheckBox { - Kirigami.FormData.label: i18n("General:") - id: deviceEnabled - text: i18n("Device enabled") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Accept input through this device.") - visible: parent.hovered - delay: 1000 - } - - function load() { - if (!formLayout.enabled) { - checked = false - return - } - enabled = touchpad.supportsDisableEvents - checked = enabled && touchpad.enabled - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.enabled = checked - root.changeSignal() - } - } - } - - Controls.CheckBox { - id: dwt - text: i18n("Disable while typing") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Disable touchpad while typing to prevent accidental inputs.") - visible: parent.hovered - delay: 1000 - } - - function load() { - if (!formLayout.enabled) { - checked = false - return - } - enabled = touchpad.supportsDisableWhileTyping - checked = enabled && touchpad.disableWhileTyping - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.disableWhileTyping = checked - root.changeSignal() - } - } - } - - Controls.CheckBox { - id: leftHanded - text: i18n("Left handed mode") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Swap left and right buttons.") - visible: parent.hovered - delay: 1000 - } - - function load() { - if (!formLayout.enabled) { - checked = false - return - } - enabled = touchpad.supportsLeftHanded - checked = enabled && touchpad.leftHanded - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.leftHanded = checked - root.changeSignal() - } - } - } - - Controls.CheckBox { - id: middleEmulation - text: i18n("Press left and right buttons for middle click") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Clicking left and right button simultaneously sends middle button click.") - visible: parent.hovered - delay: 1000 - } - - function load() { - if (!formLayout.enabled) { - checked = false - return - } - enabled = touchpad.supportsMiddleEmulation - checked = enabled && touchpad.middleEmulation - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.middleEmulation = checked - root.changeSignal() - } - } - } - - Kirigami.Separator { - } - - // Acceleration - Controls.Slider { - Kirigami.FormData.label: i18n("Pointer speed:") - id: accelSpeed - - from: 1 - to: 11 - stepSize: 1 - - function load() { - enabled = touchpad.supportsPointerAcceleration - if (!enabled) { - value = 0.1 - return - } - // transform libinput's pointer acceleration range [-1, 1] to slider range [1, 11] - value = 6 + touchpad.pointerAcceleration / 0.2 - } - - onValueChanged: { - if (touchpad != undefined && enabled && !root.loading) { - // transform slider range [1, 11] to libinput's pointer acceleration range [-1, 1] - // by *10 and /10, we ignore the floating points after 1 digit. This prevents from - // having a libinput value like 0.60000001 - touchpad.pointerAcceleration = Math.round(((value-6) * 0.2) * 10) / 10 - root.changeSignal() - } - } - } - - Layouts.ColumnLayout { - Kirigami.FormData.label: i18n("Acceleration profile:") - Kirigami.FormData.buddyFor: accelProfileFlat - id: accelProfile - spacing: Kirigami.Units.smallSpacing - - function load() { - enabled = touchpad.supportsPointerAccelerationProfileAdaptive - - if (!enabled) { - accelProfileFlat.checked = false - accelProfileAdaptive.checked = false - return - } - - if(touchpad.pointerAccelerationProfileAdaptive) { - accelProfileAdaptive.checked = true - } else { - accelProfileFlat.checked = true - } - } - - function syncCurrent() { - if (enabled && !root.loading) { - touchpad.pointerAccelerationProfileFlat = accelProfileFlat.checked - touchpad.pointerAccelerationProfileAdaptive = accelProfileAdaptive.checked - root.changeSignal() - } - } - - Controls.RadioButton { - id: accelProfileFlat - text: i18n("Flat") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Cursor moves the same distance as finger.") - visible: parent.hovered - delay: 1000 - } - onCheckedChanged: accelProfile.syncCurrent() - } - - Controls.RadioButton { - id: accelProfileAdaptive - text: i18n("Adaptive") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Cursor travel distance depends on movement speed of finger.") - visible: parent.hovered - delay: 1000 - } - onCheckedChanged: accelProfile.syncCurrent() - } - } - - Kirigami.Separator { - } - - // Tapping - Controls.CheckBox { - Kirigami.FormData.label: i18n("Tapping:") - id: tapToClick - text: i18n("Tap-to-click") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Single tap is left button click.") - visible: parent.hovered - delay: 1000 - } - - function load() { - enabled = touchpad.tapFingerCount > 0 - checked = enabled && touchpad.tapToClick - } - - function updateDependents() { - loading = true - tapAndDrag.load() - tapAndDragLock.load() - multiTap.load() - loading = false - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.tapToClick = checked - updateDependents() - root.changeSignal() - } - } - } - - Controls.CheckBox { - id: tapAndDrag - text: i18n("Tap-and-drag") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Sliding over touchpad directly after tap drags.") - visible: parent.hovered - delay: 1000 - } - - function load() { - enabled = touchpad.tapFingerCount > 0 && tapToClick.checked - checked = enabled && touchpad.tapAndDrag - } - - function updateDependents() { - loading = true - tapAndDragLock.load() - loading = false - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.tapAndDrag = checked - updateDependents() - root.changeSignal() - } - } - } - - Controls.CheckBox { - id: tapAndDragLock - text: i18n("Tap-and-drag lock") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Dragging continues after a short finger lift.") - visible: parent.hovered - delay: 1000 - } - - function load() { - enabled = touchpad.tapFingerCount > 0 && tapAndDrag.checked - checked = enabled && touchpad.tapDragLock - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.tapDragLock = checked - root.changeSignal() - } - } - } - - Layouts.ColumnLayout { - Kirigami.FormData.label: i18n("Two-finger tap:") - Kirigami.FormData.buddyFor: multiTapRightClick - id: multiTap - - spacing: Kirigami.Units.smallSpacing - - function load() { - enabled = touchpad.supportsLmrTapButtonMap && tapToClick.checked - if (touchpad.tapFingerCount > 2) { - multiTapRightClick.text = i18n("Right-click (three-finger tap to middle-click)") - multiTapRightClickToolTip.text = i18n("Tap with two fingers to right-click, tap with three fingers to middle-click.") - - multiTapMiddleClick.text = i18n("Middle-click (three-finger tap right-click)") - multiTapMiddleClickToolTip.text = i18n("Tap with two fingers to middle-click, tap with three fingers to right-click.") - } else { - multiTapRightClick.text = i18n("Righ-click") - multiTapRightClickToolTip.text = i18n("Tap with two fingers to right-click.") - - multiTapMiddleClick.text = i18n("Middle-click") - multiTapMiddleClickToolTip.text = i18n("Tap with two fingers to middle-click.") - } - - if (!enabled) { - multiTapRightClick.checked = false - multiTapMiddleClick.checked = false - return - } - - if(touchpad.lmrTapButtonMap) { - multiTapMiddleClick.checked = true - } else { - multiTapRightClick.checked = true - } - } - - function syncCurrent() { - if (enabled && !root.loading) { - touchpad.lmrTapButtonMap = multiTapMiddleClick.checked - root.changeSignal() - } - } - - Controls.RadioButton { - id: multiTapRightClick - // text: is handled dynamically on load. - - hoverEnabled: true - Controls.ToolTip { - id: multiTapRightClickToolTip - visible: parent.hovered - delay: 1000 - // text: is handled dynamically on load. - } - onCheckedChanged: multiTap.syncCurrent() - } - - Controls.RadioButton { - id: multiTapMiddleClick - // text: is handled dynamically on load. - - hoverEnabled: true - Controls.ToolTip { - id: multiTapMiddleClickToolTip - visible: parent.hovered - delay: 1000 - // text: is handled dynamically on load. - } - onCheckedChanged: multiTap.syncCurrent() - } - } - - Kirigami.Separator { - } - - // Scrolling - Layouts.ColumnLayout { - Kirigami.FormData.label: i18n("Scrolling:") - Kirigami.FormData.buddyFor: scrollMethodTwoFingers - id: scrollMethod - - spacing: Kirigami.Units.smallSpacing - - function load() { - scrollMethodTwoFingers.enabled = touchpad.supportsScrollTwoFinger - scrollMethodTouchpadEdges.enabled = touchpad.supportsScrollEdge - - if(scrollMethodTouchpadEdges.enabled && touchpad.scrollEdge) { - scrollMethodTouchpadEdges.checked = formLayout.enabled - } else { - scrollMethodTwoFingers.checked = formLayout.enabled - } - } - - function syncCurrent() { - if (enabled && !root.loading) { - touchpad.scrollTwoFinger = scrollMethodTwoFingers.checked - touchpad.scrollEdge = scrollMethodTouchpadEdges.checked - root.changeSignal() - } - loading = true - naturalScroll.load() - loading = false - } - - Controls.RadioButton { - id: scrollMethodTwoFingers - text: i18n("Two fingers") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Slide with two fingers scrolls.") - visible: parent.hovered - delay: 1000 - } - } - - Controls.RadioButton { - id: scrollMethodTouchpadEdges - text: i18n("Touchpad edges") - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Slide on the touchpad edges scrolls.") - visible: parent.hovered - delay: 1000 - } - onCheckedChanged: scrollMethod.syncCurrent() - } - } - - Controls.CheckBox { - id: naturalScroll - text: i18n("Invert scroll direction (Natural scrolling)") - - function load() { - enabled = touchpad.supportsNaturalScroll - checked = enabled && touchpad.naturalScroll - } - - onCheckedChanged: { - if (enabled && !root.loading) { - touchpad.naturalScroll = checked - root.changeSignal() - } - } - - hoverEnabled: true - Controls.ToolTip { - text: i18n("Touchscreen like scrolling.") - visible: parent.hovered - delay: 1000 - } - } - } // END Kirigami.FormLayout -} // END Kirigami.ScrollablePage diff --git a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.cpp b/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.cpp deleted file mode 100644 --- a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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. - */ - -#include "touchpadconfiglibinput.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "../touchpadconfigcontainer.h" -#include "touchpadbackend.h" - -#include "version.h" - -TouchpadConfigLibinput::TouchpadConfigLibinput(TouchpadConfigContainer *parent, const QVariantList &args) - : TouchpadConfigPlugin(parent) -{ - KAboutData* data = new KAboutData(QStringLiteral("kcm_touchpad"), - i18n("Touchpad KCM"), - TOUCHPAD_KCM_VERSION, - i18n("System Settings module for managing your touchpad"), - KAboutLicense::GPL_V2, - i18n("Copyright © 2016 Roman Gilg"), - QString()); - - data->addAuthor(i18n("Roman Gilg"), - i18n("Developer"), - QStringLiteral("subdiff@gmail.com")); - - m_parent->setAboutData(data); - - m_backend = TouchpadBackend::implementation(); - 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", QVariant::fromValue(m_backend->getDevices().toList())); - - 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(touchpadAdded(bool)), this, SLOT(onTouchpadAdded(bool))); - connect(m_backend, SIGNAL(touchpadRemoved(int)), this, SLOT(onTouchpadRemoved(int))); - connect(m_view->rootObject(), SIGNAL(changeSignal()), this, SLOT(onChange())); - } - - m_view->show(); -} - -QSize TouchpadConfigLibinput::sizeHint() const -{ - return QQmlProperty::read(m_view->rootObject(), "sizeHint").toSize(); -} - -QSize TouchpadConfigLibinput::minimumSizeHint() const -{ - return QQmlProperty::read(m_view->rootObject(), "minimumSizeHint").toSize(); -} - -void TouchpadConfigLibinput::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->touchpadCount()) { - m_errorMessage->setMessageType(KMessageWidget::Information); - m_errorMessage->setText(i18n("No touchpad found. Connect touchpad now.")); - m_errorMessage->animatedShow(); - } - } - QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend"); -} - -void TouchpadConfigLibinput::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 TouchpadConfigLibinput::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 TouchpadConfigLibinput::onChange() -{ - if (!m_backend->touchpadCount()) { - return; - } - hideErrorMessage(); - emit m_parent->changed(m_backend->isChangedConfig()); -} - -void TouchpadConfigLibinput::onTouchpadAdded(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->touchpadCount() == 1) { - // if no touchpad 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", QVariant::fromValue(m_backend->getDevices())); - QMetaObject::invokeMethod(rootObj, "resetModel", Q_ARG(QVariant, activeIndex)); - QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); -} - -void TouchpadConfigLibinput::onTouchpadRemoved(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->touchpadCount()) { - m_errorMessage->setText(i18n("Touchpad disconnected. Closed its setting dialog.")); - } else { - m_errorMessage->setText(i18n("Touchpad disconnected. No other touchpads found.")); - } - m_errorMessage->animatedShow(); - activeIndex = 0; - } else { - if (index < activeIndex) { - activeIndex--; - } - } - m_view->rootContext()->setContextProperty("deviceModel", QVariant::fromValue(m_backend->getDevices())); - QMetaObject::invokeMethod(m_view->rootObject(), "resetModel", Q_ARG(QVariant, activeIndex)); - QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); - - emit m_parent->changed(m_backend->isChangedConfig()); -} - -void TouchpadConfigLibinput::hideErrorMessage() -{ - if (m_errorMessage->isVisible()) { - m_errorMessage->animatedHide(); - } -} diff --git a/kcms/touchpad/src/kcm/resources.qrc b/kcms/touchpad/src/kcm/resources.qrc deleted file mode 100644 --- a/kcms/touchpad/src/kcm/resources.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - libinput/main.qml - - diff --git a/kcms/touchpad/src/kcm/touchpadconfigcontainer.h b/kcms/touchpad/src/kcm/touchpadconfigcontainer.h --- a/kcms/touchpad/src/kcm/touchpadconfigcontainer.h +++ b/kcms/touchpad/src/kcm/touchpadconfigcontainer.h @@ -22,15 +22,13 @@ #include class TouchpadConfigPlugin; -class TouchpadConfigLibinput; class TouchpadConfigXlib; class TouchpadConfigContainer : public KCModule { Q_OBJECT friend TouchpadConfigXlib; - friend TouchpadConfigLibinput; public: explicit TouchpadConfigContainer(QWidget *parent, diff --git a/kcms/touchpad/src/kcm/touchpadconfigcontainer.cpp b/kcms/touchpad/src/kcm/touchpadconfigcontainer.cpp --- a/kcms/touchpad/src/kcm/touchpadconfigcontainer.cpp +++ b/kcms/touchpad/src/kcm/touchpadconfigcontainer.cpp @@ -18,7 +18,6 @@ #include "touchpadconfigcontainer.h" #include "touchpadconfigplugin.h" -#include "kcm/libinput/touchpadconfiglibinput.h" #include "kcm/xlib/touchpadconfigxlib.h" #include @@ -38,8 +37,6 @@ { if (KWindowSystem::isPlatformX11()) { m_plugin = new TouchpadConfigXlib(this); - } else if (KWindowSystem::isPlatformWayland()) { - m_plugin = new TouchpadConfigLibinput(this); } } diff --git a/kcms/touchpad/src/kcm/xlib/touchpadconfigxlib.cpp b/kcms/touchpad/src/kcm/xlib/touchpadconfigxlib.cpp --- a/kcms/touchpad/src/kcm/xlib/touchpadconfigxlib.cpp +++ b/kcms/touchpad/src/kcm/xlib/touchpadconfigxlib.cpp @@ -102,8 +102,8 @@ : TouchpadConfigPlugin(parent), m_configOutOfSync(false) { - KAboutData* data = new KAboutData(QStringLiteral("kcm_touchpad"), - i18n("Touchpad KCM"), + KAboutData* data = new KAboutData(QStringLiteral("kcm_touchpadx"), + i18n("TouchpadX KCM"), TOUCHPAD_KCM_VERSION, i18n("System Settings module, daemon and Plasma applet for managing your touchpad"), KAboutLicense::GPL_V2, diff --git a/kcms/touchpad/src/touchpadbackend.cpp b/kcms/touchpad/src/touchpadbackend.cpp --- a/kcms/touchpad/src/touchpadbackend.cpp +++ b/kcms/touchpad/src/touchpadbackend.cpp @@ -20,7 +20,6 @@ #include "touchpadbackend.h" #include "backends/x11/xlibbackend.h" -#include "backends/kwin_wayland/kwinwaylandbackend.h" #include "logging.h" #include @@ -39,11 +38,6 @@ } return backend.localData().data(); } - // TODO: test on kwin_wayland specifically? What about possibly other compositors under Wayland? - else if (KWindowSystem::isPlatformWayland()) { - qCDebug(KCM_TOUCHPAD) << "Using KWin+Wayland backend"; - return (new KWinWaylandBackend()); - } else { qCCritical(KCM_TOUCHPAD) << "Not able to select appropriate backend."; return nullptr; diff --git a/kcms/touchpad/CMakeLists.txt b/kcms/touchpadx/CMakeLists.txt copy from kcms/touchpad/CMakeLists.txt copy to kcms/touchpadx/CMakeLists.txt --- a/kcms/touchpad/CMakeLists.txt +++ b/kcms/touchpadx/CMakeLists.txt @@ -1,6 +1,6 @@ find_package(KDED ${KF5_MIN_VERSION} CONFIG REQUIRED) -add_definitions(-DTRANSLATION_DOMAIN=\"kcm_touchpad\") +add_definitions(-DTRANSLATION_DOMAIN=\"kcm_touchpadx\") set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules diff --git a/kcms/touchpadx/COPYING b/kcms/touchpadx/COPYING new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/COPYING @@ -0,0 +1,347 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/kcms/touchpad/Messages.sh b/kcms/touchpadx/Messages.sh copy from kcms/touchpad/Messages.sh copy to kcms/touchpadx/Messages.sh --- a/kcms/touchpad/Messages.sh +++ b/kcms/touchpadx/Messages.sh @@ -1,6 +1,6 @@ #!/bin/sh $EXTRACTRC `find src -path src/applet -prune -o \( -name \*.rc -o -name \*.ui -o -name \*.kcfg \) -print` >> rc.cpp -$XGETTEXT rc.cpp `find src -path src/applet -prune -o \( -name \*.cpp -o -name \*.h \) -print` -o $podir/kcm_touchpad.pot +$XGETTEXT rc.cpp `find src -path src/applet -prune -o \( -name \*.cpp -o -name \*.h \) -print` -o $podir/kcm_touchpadx.pot $XGETTEXT `find src/applet -name \*.qml -o -name \*.js` -o $podir/plasma_applet_touchpad.pot diff --git a/kcms/touchpad/README.md b/kcms/touchpadx/README.md copy from kcms/touchpad/README.md copy to kcms/touchpadx/README.md --- a/kcms/touchpad/README.md +++ b/kcms/touchpadx/README.md @@ -1,10 +1,10 @@ -Touchpad KCM +TouchpadX KCM ============ -* KCModule (Wayland + X) -* Daemon (X only) +* KCModule +* Daemon - Automatically enable/disable touchpad when typing and/or when mouse is plugged in - Enable/disable touchpad with keyboard shortcuts -* Applet (X only) +* Applet - Shows touchpad's state - Toggle touchpad with single click diff --git a/kcms/touchpadx/cmake/modules/COPYING-CMAKE-SCRIPTS b/kcms/touchpadx/cmake/modules/COPYING-CMAKE-SCRIPTS new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/cmake/modules/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,23 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/kcms/touchpadx/cmake/modules/FindX11_XCB.cmake b/kcms/touchpadx/cmake/modules/FindX11_XCB.cmake new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/cmake/modules/FindX11_XCB.cmake @@ -0,0 +1,53 @@ +# - Try to find libX11-xcb +# Once done this will define +# +# X11_XCB_FOUND - system has libX11-xcb +# X11_XCB_LIBRARIES - Link these to use libX11-xcb +# X11_XCB_INCLUDE_DIR - the libX11-xcb include dir +# X11_XCB_DEFINITIONS - compiler switches required for using libX11-xcb + +# Copyright (c) 2012 Fredrik Höglund +# Copyright (c) 2008 Helio Chissini de Castro, +# Copyright (c) 2007 Matthias Kretz, +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +IF (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + FIND_PACKAGE(PkgConfig) + PKG_CHECK_MODULES(PKG_X11_XCB QUIET x11-xcb) + + SET(X11_XCB_DEFINITIONS ${PKG_X11_XCB_CFLAGS}) + + FIND_PATH(X11_XCB_INCLUDE_DIR NAMES X11/Xlib-xcb.h HINTS ${PKG_X11_XCB_INCLUDE_DIRS}) + FIND_LIBRARY(X11_XCB_LIBRARIES NAMES X11-xcb HINTS ${PKG_X11_XCB_LIBRARY_DIRS}) + + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(X11_XCB DEFAULT_MSG X11_XCB_LIBRARIES X11_XCB_INCLUDE_DIR) + + MARK_AS_ADVANCED(X11_XCB_INCLUDE_DIR X11_XCB_LIBRARIES) +ENDIF (NOT WIN32) + diff --git a/kcms/touchpadx/cmake/modules/FindXCB.cmake b/kcms/touchpadx/cmake/modules/FindXCB.cmake new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/cmake/modules/FindXCB.cmake @@ -0,0 +1,80 @@ +# Try to find XCB on a Unix system +# +# This will define: +# +# XCB_FOUND - True if xcb is available +# XCB_LIBRARIES - Link these to use xcb +# XCB_INCLUDE_DIR - Include directory for xcb +# XCB_DEFINITIONS - Compiler flags for using xcb +# +# In addition the following more fine grained variables will be defined: +# +# XCB_XCB_FOUND XCB_XCB_INCLUDE_DIR XCB_XCB_LIBRARIES +# XCB_RECORD_FOUND XCB_RECORD_INCLUDE_DIR XCB_RECORD_LIBRARIES +# +# Copyright (c) 2012 Fredrik Höglund +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + + +IF (NOT WIN32) + IF (XCB_INCLUDE_DIR AND XCB_LIBRARIES) + # In the cache already + SET(XCB_FIND_QUIETLY TRUE) + ENDIF (XCB_INCLUDE_DIR AND XCB_LIBRARIES) + + # Use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + FIND_PACKAGE(PkgConfig) + PKG_CHECK_MODULES(PKG_XCB QUIET xcb) + + SET(XCB_DEFINITIONS ${PKG_XCB_CFLAGS}) + + FIND_PATH(XCB_XCB_INCLUDE_DIR NAMES xcb/xcb.h HINTS ${PKG_XCB_INCLUDE_DIRS}) + FIND_PATH(XCB_RECORD_INCLUDE_DIR NAMES xcb/record.h HINTS ${PKG_XCB_INCLUDE_DIRS}) + + FIND_LIBRARY(XCB_XCB_LIBRARIES NAMES xcb HINTS ${PKG_XCB_LIBRARY_DIRS}) + FIND_LIBRARY(XCB_RECORD_LIBRARIES NAMES xcb-record HINTS ${PKG_XCB_LIBRARY_DIRS}) + + set(XCB_INCLUDE_DIR ${XCB_XCB_INCLUDE_DIR} ${XCB_RECORD_INCLUDE_DIR}) + + set(XCB_LIBRARIES ${XCB_XCB_LIBRARIES} ${XCB_RECORD_LIBRARIES}) + + list(REMOVE_DUPLICATES XCB_INCLUDE_DIR) + + include(FindPackageHandleStandardArgs) + + FIND_PACKAGE_HANDLE_STANDARD_ARGS(XCB_XCB DEFAULT_MSG XCB_XCB_LIBRARIES XCB_XCB_INCLUDE_DIR) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(XCB_RECORD DEFAULT_MSG XCB_RECORD_LIBRARIES XCB_RECORD_INCLUDE_DIR) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(XCB DEFAULT_MSG XCB_LIBRARIES XCB_INCLUDE_DIR) + + MARK_AS_ADVANCED( + XCB_INCLUDE_DIR XCB_LIBRARIES + XCB_XCB_INCLUDE_DIR XCB_XCB_LIBRARIES + XCB_RECORD_INCLUDE_DIR XCB_RECORD_LIBRARIES + ) + +ENDIF (NOT WIN32) + diff --git a/kcms/touchpadx/icon/128-devices-input-touchpad.png b/kcms/touchpadx/icon/128-devices-input-touchpad.png new file mode 100644 index 0000000000000000000000000000000000000000..ffc3d8707ac1167d6709d5415121029203bb919b GIT binary patch literal 5303 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 literal 0 Hc$@14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>dl)sZ{50e$BrF9GB!3gIXQXJqD4TXjvhUF@#4ixmoA++ zaboi1$unlmNKH+xudiRVYSsGn>swn}OG-+BW_EOR01fx@@&X#((9lp120(LygM)!0 zKq(+uP*4DLBm)dQdi3bcn>VjtzkdGw`GW@!UcP+!>eZ{KPoKVh`}Y0&_a8od`1tYT zyLazCfByXS>(_7JzJ2-f<y|BBHfz?b{{DWT?|^PTdGh4`{riDVU9x1!ym|AUJb422&i(uM@7=q1_3Bli zGkm_JuK-dGB|(0`Xp!;q^HE*`G;#a+3+FBX(fJEIzJ3OQmmg28{SI`=*YB5ap4@f! z?uE5?@1C^koG@WxWy{hfOJ;d9GBXK#x}}1mCCS^}g`tC0)&t1lEbxdd2Bw4GAk4^J zZaNz%c-zy(F+}2Wa)JZ9&lw$$hN-=cbDM=Z3kr=tSXfvoHW=$O&+lHndiG>x0SjPE z?)uEkdS{M}&E7qG_wKE+nR9@t&+gv7x_|%vF*e58Dd;-d#qIldP*HK=!MetIiA+3A zd8kxvX!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$Ysfq`*$fKP}kP=SGgft#CK zR8&+G}*zyp`f4usAj^135ymj+OT26 zjvYI8?%cU?<3=F2q@<*xq5`NMs0+xQGG$6@Yim_iRb^$Rlao_GK*03r(-$sW7!eT> z7#P^m(b3b>E2|6!Dk>`4+S+=0dRkgqnwpvrL7@4UFJHcR@gmS|Cr+Gr^XAQ~SFc{b zetrG=_2b8nzkK=f+O=zM-@bkS{{4p!A3lEk`03N9&!0bk`SRuK*RS8cef$3X`;Q+# ze*XOV?%lgzzkdDx{rk_KKR0jQ{QLLsjT<+P9Xqys`Ep>e0DU)S&K#fzySuxao11~* z1`L+Pix=nT=U=*X>Ey|ihYlTj`t<4J$B)mPIRlIrzdPB%Kq|Z>$S;^dv3>sBx$`FK z_pjc$YsW4i*tu)Y=TA`Z?A_Xyx9=d}!@d*$AYj$3ix+nR@x+T4cQ~DU`t-&2ywf(ZY}vVkrPT+j!t)Euf86#d-^rr)koo*{v-8$7J(qL*?OyR> z#*H2KkEp9GT2lMpk>%gvlqo_7VpvpWbk(>&xpCC=-rFb9&aBTQXE=yld%zNy)WPI! z{pb=%hRlCo0!?+flR=m%Zqd>IzM|CL2sQsJG`3{o{m_ptZ} zp15GZ-`)^w`^oKwlR=%l?C%fLoa~NYJo4nqmeXh6-1)1GpWnkcaC!-2QLvDUbW?Cg~4OWd8jsZ1Df;0r@rERK(!v>gTe~DWM4fb_)>; literal 0 Hc$@rZOn~R*Tvp3bwOcN(yZ)Wq{CfmkN{?ECxaiqF6VW z+0+2r5|kjwl#2rf4Yvuw!dIYJlx9OvQ0{0!RZ|d!FYx=PO_Z z9o=NT-5MdZ$&Wz`foT7BSee21K_#w&U>@(o^g*b$kYI?mfVs1f5yC_$>kEW(^AK8t zrQ9imq$Gr9q7b59MQB@UX>+g_LMC&pKn@)?T)cRZNF-tycJScALx&D|dwY|~WOsLW z3WXw*$Sp`l@6VG$7#k&%(%;o)9hUeKYcs_N#=o3*vIjg5^; zrLwcLv%9-nrBbQY>cPRmfq{X5fB;WVPdc5>WHO;cQc}|C)2E?%VPRoWQISHSkjv#_ zu{bR)?d;jJd3kx++1Zc_g+dO8110|c{!s4g>swh_IW#o%@ZrPp@$sptsfC4w=g*(d z&(A-5_AEa?A6hXO3^)ZSUA}xdFfb7E%*;%wR0SM3SDK4rF*d9#~=i0hX4Qj*gD5uC7i_PLA(jXJ=PeS65$O-`v~` zOy=h1KrhIjot>SYo}QVR0b+}bi@@;3ix+ymeraiGd3pKe%a^ZSy;@mWSzTRy{rWYS zv$nQoFc`q7_4V~PZ{8SBqWH9 z&_dqxo=l_Y0$(9Y|PxjR1LOd}JAPYtAvS9sjevq~uDWiQam}eubd8fE^B& zqwuopR;K>`?Y3uZ3NDoyG)g5%P%1TsGTSC|R#UaI%+}mh)vMPloNZU><+4dTu9iai zokG!a?IsULL=|WZO1-M8Sl0~xH|H16sPszZH#>@GiF`Vjc_e}juYjF>gvq7z`3e%t zWwT50Bi-=mu=P(HUCQC(TcfZ#aSE=;+ttOUO z+6KQpi4Ob&G7Eb7t%YAkTa6n&-o-o?#|q}M9}L{r3=e9D_FWYu#3T!n;!a}POC2g* z_po}jWAL7=STVb7FgTForJwYvGlZ$XvX4b-S&ul!OBToZqq;C2`v)F}`asdqLT+CROL!<`|da)uo$F38tpoh+%s_?fLnS-Mh~3*#FsHH#bMCP8XMJ zWkP{KAa^Eu%jFoh$H6otWJp7Hb|w=tT})6ce%Bm7Rw0GE`dmT}mBd6#VyQxLEClow z2_w5>BzNopheV-bo>a>IFG>5TBvQa%30eOs5T(Q<#>xJ_fGQv8h60DT1?-eKNoKS- j7QydIOgwoaT^OAjOH2{Rmn?W~hfT literal 0 Hc$@?*o7XkzY1wm7aljHBtnm3rJO3 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 U^evn*IRKXsTPsImjfH38ADe6CR{#J2 literal 0 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 literal 0 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 literal 0 Hc$@ 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 WFEtiFp5ML0pZ^aF^DUdf(*OWf(AQxA literal 0 Hc$@ + * Copyright 2016 Kai Uwe Broulik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library 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.0 +import QtQuick.Layouts 1.1 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +import org.kde.plasma.plasmoid 2.0 + +Item { + id: root + + readonly property bool hasTouchpad: typeof dataSource.data.touchpad !== "undefined" && dataSource.data.touchpad.workingTouchpadFound + readonly property bool touchpadEnabled: hasTouchpad ? dataSource.data.touchpad.enabled : false + readonly property bool hasMouse: hasTouchpad ? dataSource.data.touchpad.mousePluggedIn : false + + Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation + Plasmoid.icon: touchpadEnabled ? "input-touchpad-on" : "input-touchpad-off" + Plasmoid.status: { + if (confirmDialog.status !== PlasmaComponents.DialogStatus.Closed) { + return PlasmaCore.Types.AcceptingInputStatus; + } else if (hasTouchpad) { + return PlasmaCore.Types.ActiveStatus; + } else { + return PlasmaCore.Types.HiddenStatus; + } + } + Plasmoid.toolTipSubText: { + if (!hasTouchpad) { + return i18n("No touchpad was found"); + } else if (touchpadEnabled) { + return i18n("Touchpad is enabled"); + } else { + return i18n("Touchpad is disabled"); + } + } + Plasmoid.onActivated: action_toggle() + + Component.onCompleted: { + // the "text" argument for setAction is mandatory but we overwrite + // it by a binding right away anyway + plasmoid.setAction("toggle", ""); + + var action = plasmoid.action("toggle"); + action.text = Qt.binding(function() { + return root.touchpadEnabled ? i18n("Disable touchpad") : i18n("Enable touchpad"); + }); + action.visible = Qt.binding(function() { + return root.hasTouchpad; + }); + } + + PlasmaCore.DataSource { + id: dataSource + engine: "touchpad" + connectedSources: dataSource.sources + } + + Plasmoid.compactRepresentation: PlasmaCore.ToolTipArea { + id: toolTip + + Layout.minimumWidth: units.iconSizes.small + Layout.minimumHeight: Layout.minimumWidth + + icon: plasmoid.icon + mainText: plasmoid.title + subText: plasmoid.toolTipSubText + + active: confirmDialog.status === PlasmaComponents.DialogStatus.Closed + + Connections { + target: confirmDialog + onStatusChanged: { + if (confirmDialog.status === PlasmaComponents.DialogStatus.Open) { + toolTip.hideToolTip() + } + } + } + + MouseArea { + anchors.fill: parent + enabled: root.hasTouchpad + onClicked: root.action_toggle() + } + + PlasmaCore.IconItem { + anchors.fill: parent + source: plasmoid.icon + active: parent.containsMouse + enabled: root.hasTouchpad + } + } + + // This is only accessible from System Tray, when hidden in the popup + // and you click the list item text instead of the icon + Plasmoid.fullRepresentation: Item { + + PlasmaComponents.Button { + readonly property QtObject action: plasmoid.action("toggle") + anchors.centerIn: parent + text: action.text + enabled: action.visible + onClicked: action.trigger() + } + + } + + function action_toggle() { + if (!root.hasTouchpad) { + return; + } + + if (!root.hasMouse && root.touchpadEnabled) { + confirmDialog.open() + return; + } + + execOp("toggle") + } + + function execOp(op) { + var service = dataSource.serviceForSource("touchpad") + service.startOperationCall(service.operationDescription(op)) + } + + PlasmaComponents.QueryDialog { + id: confirmDialog + visualParent: plasmoid.compactRepresentationItem + titleText: i18n("Touchpad") + titleIcon: "dialog-warning" + message: i18n("No mouse was detected.\nAre you sure you want to disable the touchpad?") + acceptButtonText: i18n("Disable") + onAccepted: execOp("disable") + } +} diff --git a/kcms/touchpad/src/applet/qml/metadata.desktop b/kcms/touchpadx/src/applet/qml/metadata.desktop copy from kcms/touchpad/src/applet/qml/metadata.desktop copy to kcms/touchpadx/src/applet/qml/metadata.desktop --- a/kcms/touchpad/src/applet/qml/metadata.desktop +++ b/kcms/touchpadx/src/applet/qml/metadata.desktop @@ -136,6 +136,6 @@ X-KDE-PluginInfo-Website=https://projects.kde.org/projects/playground/utils/kcm-touchpad/ X-KDE-ServiceTypes=Plasma/Applet X-Plasma-API=declarativeappletscript -X-Plasma-ConfigPlugins=kcm_touchpad +X-Plasma-ConfigPlugins=kcm_touchpadx X-Plasma-MainScript=ui/touchpad.qml X-Plasma-NotificationArea=true diff --git a/kcms/touchpadx/src/applet/touchpad.operations b/kcms/touchpadx/src/applet/touchpad.operations new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/applet/touchpad.operations @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/kcms/touchpadx/src/applet/touchpad.svg b/kcms/touchpadx/src/applet/touchpad.svg new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/applet/touchpad.svg @@ -0,0 +1,30 @@ + + + + + + + + + + image/svg+xml + + + + + + + + touchpad_enabled + + + + + + touchpad_disabled + + + + + + diff --git a/kcms/touchpadx/src/applet/touchpadengine.h b/kcms/touchpadx/src/applet/touchpadengine.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/applet/touchpadengine.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 TOUCHPADENGINE_H +#define TOUCHPADENGINE_H + +#include + +class OrgKdeTouchpadInterface; + +class TouchpadEngine : public Plasma::DataEngine +{ + Q_OBJECT + +public: + TouchpadEngine(QObject *parent, const QVariantList &args); + ~TouchpadEngine(); + + Plasma::Service *serviceForSource(const QString &source) override; + +private Q_SLOTS: + void workingTouchpadFoundChanged(bool); + void mousePluggedInChanged(bool); + void enabledChanged(bool); + +private: + void init(); + QString m_source; + OrgKdeTouchpadInterface *m_daemon; +}; + +#endif // TOUCHPADENGINE_H diff --git a/kcms/touchpadx/src/applet/touchpadengine.cpp b/kcms/touchpadx/src/applet/touchpadengine.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/applet/touchpadengine.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "touchpadengine.h" + +#include +#include + +#include "touchpadinterface.h" +#include "touchpadservice.h" +#include "kded5interface.h" + +TouchpadEngine::TouchpadEngine(QObject *parent, const QVariantList &args) + : Plasma::DataEngine(parent, args), m_source("touchpad"), m_daemon(0) +{ + init(); +} + +void TouchpadEngine::init() +{ + OrgKdeKded5Interface kded(QLatin1String("org.kde.kded5"), + QLatin1String("/kded"), + QDBusConnection::sessionBus()); + kded.loadModule("touchpad").waitForFinished(); + + m_daemon = new OrgKdeTouchpadInterface("org.kde.kded5", "/modules/touchpad", + QDBusConnection::sessionBus(), this); + if (!m_daemon->isValid()) { + return; + } + + connect(m_daemon, SIGNAL(workingTouchpadFoundChanged(bool)), SLOT(workingTouchpadFoundChanged(bool))); + connect(m_daemon, SIGNAL(enabledChanged(bool)), SLOT(enabledChanged(bool))); + connect(m_daemon, SIGNAL(mousePluggedInChanged(bool)), + SLOT(mousePluggedInChanged(bool))); + + workingTouchpadFoundChanged(m_daemon->workingTouchpadFound()); + enabledChanged(m_daemon->isEnabled()); + mousePluggedInChanged(m_daemon->isMousePluggedIn()); +} + +void TouchpadEngine::workingTouchpadFoundChanged(bool value) +{ + setData(m_source, "workingTouchpadFound", value); +} + +void TouchpadEngine::mousePluggedInChanged(bool value) +{ + setData(m_source, "mousePluggedIn", value); +} + +void TouchpadEngine::enabledChanged(bool value) +{ + setData(m_source, "enabled", value); +} + +Plasma::Service *TouchpadEngine::serviceForSource(const QString &source) +{ + if (source == m_source) { + return new TouchpadService(m_daemon, source, this); + } + + return Plasma::DataEngine::serviceForSource(source); +} + +TouchpadEngine::~TouchpadEngine() +{ +} + +K_EXPORT_PLASMA_DATAENGINE_WITH_JSON(touchpad, TouchpadEngine, "plasma-dataengine-touchpad.json") + +#include "touchpadengine.moc" diff --git a/kcms/touchpadx/src/applet/touchpadservice.h b/kcms/touchpadx/src/applet/touchpadservice.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/applet/touchpadservice.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 TOUCHPADSERVICE_H +#define TOUCHPADSERVICE_H + +#include + +class OrgKdeTouchpadInterface; + +class TouchpadService : public Plasma::Service +{ + Q_OBJECT +public: + TouchpadService(OrgKdeTouchpadInterface *daemon, const QString &destination, + QObject *parent = 0); + ~TouchpadService(); + +protected: + Plasma::ServiceJob *createJob(const QString &operation, + QMap ¶meters) override; + +private: + OrgKdeTouchpadInterface *m_daemon; + QString m_destination; +}; + +#endif // TOUCHPADSERVICE_H diff --git a/kcms/touchpadx/src/applet/touchpadservice.cpp b/kcms/touchpadx/src/applet/touchpadservice.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/applet/touchpadservice.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "touchpadservice.h" + +#include + +#include "touchpadinterface.h" + +TouchpadService::TouchpadService(OrgKdeTouchpadInterface *daemon, + const QString &destination, + QObject *parent) + : Plasma::Service(parent), m_daemon(daemon), m_destination(destination) +{ + setName("touchpad"); +} + +TouchpadService::~TouchpadService() +{ +} + +class TouchpadJob : public Plasma::ServiceJob +{ +public: + TouchpadJob(OrgKdeTouchpadInterface *daemon, + const QString &destination, const QString &operation, + const QMap ¶meters, QObject *parent = 0) + : Plasma::ServiceJob(destination, operation, parameters, parent), + m_daemon(daemon) + { + } + + void start() override + { + if (m_daemon) { + QMetaObject::invokeMethod(m_daemon, operationName().toLatin1()); + } + emitResult(); + } + +private: + OrgKdeTouchpadInterface *m_daemon; +}; + +Plasma::ServiceJob *TouchpadService::createJob(const QString &operation, + QMap ¶ms) +{ + return new TouchpadJob(m_daemon, m_destination, operation, params, this); +} diff --git a/kcms/touchpadx/src/backends/x11.cmake b/kcms/touchpadx/src/backends/x11.cmake new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11.cmake @@ -0,0 +1,48 @@ +# // krazy:excludeall=copyright,license +find_package(X11 REQUIRED) +find_package(X11_XCB REQUIRED) +find_package(XCB REQUIRED) +find_package(PkgConfig REQUIRED) + +if(NOT X11_Xinput_FOUND) + message(FATAL_ERROR "Xinput not found") +endif() + +include_directories(${XCB_INCLUDE_DIR} + ${X11_XCB_INCLUDE_DIR} + ${X11_Xinput_INCLUDE_PATH} + ${X11_X11_INCLUDE_PATH} + ${Synaptics_INCLUDE_DIRS} + ${XORG_INCLUDE_DIRS} +) + +add_definitions(${X11_XCB_DEFINITIONS} ${XCB_DEFINITIONS}) + +SET(backend_SRCS + ${backend_SRCS} + backends/x11/propertyinfo.cpp + backends/x11/xlibbackend.cpp + backends/x11/synapticstouchpad.cpp + backends/x11/libinputtouchpad.cpp + backends/x11/xlibtouchpad.cpp + backends/x11/xcbatom.cpp + backends/x11/xlibnotifications.cpp + backends/x11/xrecordkeyboardmonitor.cpp +) + +SET(backend_LIBS + ${backend_LIBS} + ${XCB_LIBRARIES} + ${X11_X11_LIB} + ${X11_XCB_LIBRARIES} + ${X11_Xinput_LIB} +) + +add_executable(kcm-touchpad-list-devices backends/x11/listdevices.cpp) +target_link_libraries(kcm-touchpad-list-devices + ${X11_X11_LIB} + ${X11_Xinput_LIB} +) +install(TARGETS kcm-touchpad-list-devices + DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} +) diff --git a/kcms/touchpadx/src/backends/x11/libinputtouchpad.h b/kcms/touchpadx/src/backends/x11/libinputtouchpad.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/libinputtouchpad.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 Weng Xuetian + * + * 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 LIBINPUTTOUCHPAD_H +#define LIBINPUTTOUCHPAD_H + +#include "xlibtouchpad.h" + +class LibinputTouchpad : public XlibTouchpad +{ +public: + LibinputTouchpad(Display *display, int deviceId); +}; + +#endif // LIBINPUTTOUCHPAD_H diff --git a/kcms/touchpadx/src/backends/x11/libinputtouchpad.cpp b/kcms/touchpadx/src/backends/x11/libinputtouchpad.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/libinputtouchpad.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * 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 "libinputtouchpad.h" + +#include +#include + +const struct Parameter libinputProperties[] = { + /* This is a boolean for all three fingers, no per-finger config */ + {"Tapping", PT_INT, 0, 1, "libinput Tapping Enabled", 8, 0}, + /* libinput normalizes the accel to -1/1 */ + {"AccelFactor", PT_DOUBLE, -1.0, 1.0, "libinput Accel Speed", 0 /*float */, 0}, + /* Only one of these may be set at one time */ + {"VertEdgeScroll", PT_INT, 0, 1, "libinput Scroll Method Enabled", 8, 1}, + {"VertTwoFingerScroll", PT_INT, 0, 1, "libinput Scroll Method Enabled", 8, 0}, + {"InvertVertScroll", PT_INT, 0, 1, "libinput Natural Scrolling Enabled", 8, 0}, + /* libinput doesn't have a separate toggle for horiz scrolling */ + { NULL, PT_INT, 0, 0, 0, 0, 0 } +}; + +LibinputTouchpad::LibinputTouchpad(Display *display, int deviceId): XlibTouchpad(display, deviceId) +{ + loadSupportedProperties(libinputProperties); + + /* FIXME: has a different format than Synaptics Off but we don't expose + the toggle so this is just to stop it from crashing when we check + m_touchpadOffAtom */ + m_touchpadOffAtom.intern(m_connection, + "libinput Send Events Mode enabled"); + + + XcbAtom scroll_methods(m_connection, + "libinput Scroll Methods Available", + true); + if (scroll_methods.atom() != 0) { + PropertyInfo methods(m_display, + m_deviceId, + scroll_methods.atom(), + 0); + if (!methods.value(0).toInt()) + m_supported.removeAll("VertTwoFingerScroll"); + else if (!methods.value(1).toInt()) + m_supported.removeAll("VertEdgeScroll"); + } +} diff --git a/kcms/touchpadx/src/backends/x11/listdevices.cpp b/kcms/touchpadx/src/backends/x11/listdevices.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/listdevices.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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. + */ + +//Debug tool +//Must NOT be translated/localized + +#include + +#include +#include + +int main(void) +{ + Display *display = XOpenDisplay(0); + int nDevices = 0; + XDeviceInfo *devices = XListInputDevices(display, &nDevices); + for (int i = 0; i < nDevices; i++) { + const char *name = devices[i].name; + char *type = 0; + if (devices[i].type) { + type = XGetAtomName(display, devices[i].type); + } + const char *use; + switch (devices[i].use) { + case IsXPointer: use = "Pointer"; break; + case IsXKeyboard: use = "Keyboard"; break; + case IsXExtensionDevice: use = "Extension Device"; break; + case IsXExtensionPointer: use = "Extension Pointer"; break; + case IsXExtensionKeyboard: use = "Extension Keyboard"; break; + default: use = "Unknown"; + } + std::cout << "Name: " << name << " Type: " << (type ? type : "unknown") + << " Use: " << use << std::endl; + XFree(type); + } + XFreeDeviceList(devices); + XCloseDisplay(display); +} diff --git a/kcms/touchpadx/src/backends/x11/propertyinfo.h b/kcms/touchpadx/src/backends/x11/propertyinfo.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/propertyinfo.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 PROPERTYINFO_H +#define PROPERTYINFO_H + +#include +#include +#include + +void XDeleter(void *p); + +struct PropertyInfo +{ + Atom type; + int format; + QSharedPointer data; + unsigned long nitems; + + float *f; + int *i; + char *b; + + Display *display; + int device; + Atom prop; + + PropertyInfo(); + PropertyInfo(Display *display, int device, Atom prop, Atom floatType); + QVariant value(unsigned offset) const; + + void set(); +}; + +#endif // PROPERTYINFO_H diff --git a/kcms/touchpadx/src/backends/x11/propertyinfo.cpp b/kcms/touchpadx/src/backends/x11/propertyinfo.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/propertyinfo.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "propertyinfo.h" + +#include + +#include +#include +#include + +void XDeleter(void* p) +{ + if (p) { + XFree(p); + } +} + +PropertyInfo::PropertyInfo() : + type(0), format(0), nitems(0), f(0), i(0), b(0), + display(0), device(0), prop(0) +{ +} + +PropertyInfo::PropertyInfo(Display *display, int device, Atom prop, Atom floatType) + : type(0), format(0), nitems(0), f(0), i(0), b(0), + display(display), device(device), prop(prop) +{ + unsigned char *dataPtr = 0; + unsigned long bytes_after; + XIGetProperty(display, device, prop, 0, 1000, False, + AnyPropertyType, &type, &format, &nitems, + &bytes_after, &dataPtr); + data = QSharedPointer(dataPtr, XDeleter); + + if (format == CHAR_BIT && type == XA_INTEGER) { + b = reinterpret_cast(dataPtr); + } + if (format == sizeof(int) * CHAR_BIT && + (type == XA_INTEGER || type == XA_CARDINAL)) + { + i = reinterpret_cast(dataPtr); + } + if (format == sizeof(float) * CHAR_BIT && + floatType && type == floatType) + { + f = reinterpret_cast(dataPtr); + } +} + +QVariant PropertyInfo::value(unsigned offset) const +{ + QVariant v; + if (offset >= nitems) { + return v; + } + + if (b) { + v = QVariant(static_cast(b[offset])); + } + if (i) { + v = QVariant(i[offset]); + } + if (f) { + v = QVariant(f[offset]); + } + + return v; +} + +void PropertyInfo::set() +{ + XIChangeProperty(display, device, prop, type, format, + XIPropModeReplace, data.data(), nitems); +} diff --git a/kcms/touchpadx/src/backends/x11/synapticstouchpad.h b/kcms/touchpadx/src/backends/x11/synapticstouchpad.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/synapticstouchpad.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 Weng Xuetian + * + * 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 SYNAPTICSTOUCHPAD_H +#define SYNAPTICSTOUCHPAD_H + +#include "xlibtouchpad.h" +#include "xcbatom.h" + +class SynapticsTouchpad : public XlibTouchpad +{ +public: + SynapticsTouchpad(Display *display, int deviceId); + +protected: + double getPropertyScale(const QString &name) const override; + +private: + XcbAtom m_capsAtom; + int m_resX, m_resY; + QStringList m_scaleByResX, m_scaleByResY, m_toRadians; +}; + +#endif // SYNAPTICSTOUCHPAD_H diff --git a/kcms/touchpadx/src/backends/x11/synapticstouchpad.cpp b/kcms/touchpadx/src/backends/x11/synapticstouchpad.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/synapticstouchpad.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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. + */ + +/* +//krazy:excludeall=copyright + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright © 2002-2005,2007 Peter Osterlund + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Authors: + * Peter Osterlund (petero2@telia.com) + */ + +#include +#include + +#include "synapticstouchpad.h" + +#include +#include +#include + +#define SYN_MAX_BUTTONS 12 + +const struct Parameter synapticsProperties[] = { + {"LeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 0}, + {"RightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 1}, + {"TopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 2}, + {"BottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 3}, + {"FingerLow", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 0}, + {"FingerHigh", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 1}, + {"MaxTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_TIME, 32, 0}, + {"MaxTapMove", PT_INT, 0, 2000, SYNAPTICS_PROP_TAP_MOVE, 32, 0}, + {"MaxDoubleTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 1}, + {"SingleTapTimeout", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 0}, + {"ClickTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 2}, + {"FastTaps", PT_BOOL, 0, 1, SYNAPTICS_PROP_TAP_FAST, 8, 0}, + {"EmulateMidButtonTime", PT_INT, 0, 1000, SYNAPTICS_PROP_MIDDLE_TIMEOUT,32, 0}, + {"EmulateTwoFingerMinZ", PT_INT, 0, 1000, SYNAPTICS_PROP_TWOFINGER_PRESSURE, 32, 0}, + {"EmulateTwoFingerMinW", PT_INT, 0, 15, SYNAPTICS_PROP_TWOFINGER_WIDTH, 32, 0}, + {"VertScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 0}, + {"HorizScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 1}, + {"VertEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 0}, + {"HorizEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 1}, + {"CornerCoasting", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 2}, + {"VertTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 0}, + {"HorizTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 1}, + {"MinSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 0}, + {"MaxSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 1}, + {"AccelFactor", PT_DOUBLE, 0, 1.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 2}, + /*{"TouchpadOff", PT_INT, 0, 2, SYNAPTICS_PROP_OFF, 8, 0},*/ + {"LockedDrags", PT_BOOL, 0, 1, SYNAPTICS_PROP_LOCKED_DRAGS, 8, 0}, + {"LockedDragTimeout", PT_INT, 0, 30000, SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT, 32, 0}, + {"RTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 0}, + {"RBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 1}, + {"LTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 2}, + {"LBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 3}, + {"OneFingerTapButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 4}, + {"TwoFingerTapButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 5}, + {"ThreeFingerTapButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 6}, + {"ClickFinger1", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 0}, + {"ClickFinger2", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 1}, + {"ClickFinger3", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 2}, + {"CircularScrolling", PT_BOOL, 0, 1, SYNAPTICS_PROP_CIRCULAR_SCROLLING, 8, 0}, + {"CircScrollDelta", PT_DOUBLE, .01, 3, SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST, 0 /* float */, 0}, + {"CircScrollTrigger", PT_INT, 0, 8, SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER, 8, 0}, + {"PalmDetect", PT_BOOL, 0, 1, SYNAPTICS_PROP_PALM_DETECT, 8, 0}, + {"PalmMinWidth", PT_INT, 0, 15, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 0}, + {"PalmMinZ", PT_INT, 0, 255, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 1}, + {"CoastingSpeed", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 0}, + {"CoastingFriction", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 1}, + {"PressureMotionMinZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 0}, + {"PressureMotionMaxZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 1}, + {"PressureMotionMinFactor", PT_DOUBLE, 0, 10.0,SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 0}, + {"PressureMotionMaxFactor", PT_DOUBLE, 0, 10.0,SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 1}, + {"GrabEventDevice", PT_BOOL, 0, 1, SYNAPTICS_PROP_GRAB, 8, 0}, + {"TapAndDragGesture", PT_BOOL, 0, 1, SYNAPTICS_PROP_GESTURES, 8, 0}, + {"AreaLeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 0}, + {"AreaRightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 1}, + {"AreaTopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 2}, + {"AreaBottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 3}, + {"HorizHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 0}, + {"VertHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 1}, + {"ClickPad", PT_BOOL, 0, 1, SYNAPTICS_PROP_CLICKPAD, 8, 0}, + {"RightButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 0}, + {"RightButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 1}, + {"RightButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 2}, + {"RightButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 3}, + {"MiddleButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 4}, + {"MiddleButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 5}, + {"MiddleButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 6}, + {"MiddleButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 7}, + { NULL, PT_INT, 0, 0, 0, 0, 0 } +}; + +SynapticsTouchpad::SynapticsTouchpad(Display *display, int deviceId): XlibTouchpad(display, deviceId), + m_resX(1), m_resY(1) +{ + m_capsAtom.intern(m_connection, SYNAPTICS_PROP_CAPABILITIES); + m_touchpadOffAtom.intern(m_connection, SYNAPTICS_PROP_OFF); + XcbAtom resolutionAtom(m_connection, SYNAPTICS_PROP_RESOLUTION); + XcbAtom edgesAtom(m_connection, SYNAPTICS_PROP_EDGES); + + loadSupportedProperties(synapticsProperties); + + m_toRadians.append("CircScrollDelta"); + + PropertyInfo edges(m_display, m_deviceId, edgesAtom, 0); + if (edges.i && edges.nitems == 4) { + int w = qAbs(edges.i[1] - edges.i[0]); + int h = qAbs(edges.i[3] - edges.i[2]); + m_resX = w / 90; + m_resY = h / 50; + qDebug() << "Width: " << w << " height: " << h; + qDebug() << "Approx. resX: " << m_resX << " resY: " << m_resY; + } + + PropertyInfo resolution(m_display, m_deviceId, resolutionAtom, 0); + if (resolution.i && resolution.nitems == 2 && + resolution.i[0] > 1 && resolution.i[1] > 1) + { + m_resY = qMin(static_cast(resolution.i[0]), + static_cast(INT_MAX)); + m_resX = qMin(static_cast(resolution.i[1]), + static_cast(INT_MAX)); + qDebug() << "Touchpad resolution: x: " << m_resX << " y: " << m_resY; + } + + m_scaleByResX.append("HorizScrollDelta"); + m_scaleByResY.append("VertScrollDelta"); + m_scaleByResX.append("MaxTapMove"); + m_scaleByResY.append("MaxTapMove"); + + m_resX = qMax(10, m_resX); + m_resY = qMax(10, m_resY); + qDebug() << "Final resolution x:" << m_resX << " y:" << m_resY; + m_negate["HorizScrollDelta"] = "InvertHorizScroll"; + m_negate["VertScrollDelta"] = "InvertVertScroll"; + m_supported.append(m_negate.values()); + m_supported.append("Coasting"); + + PropertyInfo caps(m_display, m_deviceId, m_capsAtom.atom(), 0); + if (!caps.b) { + return; + } + + enum TouchpadCapabilitiy + { + TouchpadHasLeftButton, + TouchpadHasMiddleButton, + TouchpadHasRightButton, + TouchpadTwoFingerDetect, + TouchpadThreeFingerDetect, + TouchpadPressureDetect, + TouchpadPalmDetect, + TouchpadCapsCount + }; + + QVector cap(TouchpadCapsCount, false); + qCopy(caps.b, caps.b + qMin(cap.size(), static_cast(caps.nitems)), + cap.begin()); + + if (!cap[TouchpadTwoFingerDetect]) { + m_supported.removeAll("HorizTwoFingerScroll"); + m_supported.removeAll("VertTwoFingerScroll"); + m_supported.removeAll("TwoFingerTapButton"); + } + + if (!cap[TouchpadThreeFingerDetect]) { + m_supported.removeAll("ThreeFingerTapButton"); + } + + if (!cap[TouchpadPressureDetect]) { + m_supported.removeAll("FingerHigh"); + m_supported.removeAll("FingerLow"); + + m_supported.removeAll("PalmMinZ"); + m_supported.removeAll("PressureMotionMinZ"); + m_supported.removeAll("PressureMotionMinFactor"); + m_supported.removeAll("PressureMotionMaxZ"); + m_supported.removeAll("PressureMotionMaxFactor"); + m_supported.removeAll("EmulateTwoFingerMinZ"); + } + + if (!cap[TouchpadPalmDetect]) { + m_supported.removeAll("PalmDetect"); + m_supported.removeAll("PalmMinWidth"); + m_supported.removeAll("PalmMinZ"); + m_supported.removeAll("EmulateTwoFingerMinW"); + } + + for (QMap::Iterator i = m_negate.begin(); + i != m_negate.end(); ++i) + { + if (!m_supported.contains(i.key())) { + m_supported.removeAll(i.value()); + } + } + + m_paramList = synapticsProperties; +} + +double SynapticsTouchpad::getPropertyScale(const QString &name) const +{ + if (m_scaleByResX.contains(name) && m_scaleByResY.contains(name)) { + return std::sqrt(static_cast(m_resX) * m_resX + + static_cast(m_resY) * m_resY); + } else if (m_scaleByResX.contains(name)) { + return m_resX; + } else if (m_scaleByResY.contains(name)) { + return m_resY; + } else if (m_toRadians.contains(name)) { + return M_PI_4 / 45.0; + } + return 1.0; +} diff --git a/kcms/touchpadx/src/backends/x11/xcbatom.h b/kcms/touchpadx/src/backends/x11/xcbatom.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xcbatom.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 XCBATOM_H +#define XCBATOM_H + +#include + +class XcbAtom +{ +public: + XcbAtom(); + XcbAtom(xcb_connection_t *, const char *name, bool onlyIfExists = true); + ~XcbAtom(); + + void intern(xcb_connection_t *, const char *name, bool onlyIfExists = true); + xcb_atom_t atom(); + operator xcb_atom_t() { return atom(); } + +private: + XcbAtom(const XcbAtom &); + XcbAtom &operator =(const XcbAtom &); + + xcb_connection_t *m_connection; + xcb_intern_atom_cookie_t m_cookie; + xcb_intern_atom_reply_t *m_reply; + bool m_fetched; +}; + +#endif // XCBATOM_H diff --git a/kcms/touchpadx/src/backends/x11/xcbatom.cpp b/kcms/touchpadx/src/backends/x11/xcbatom.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xcbatom.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "xcbatom.h" + +#include +#include + +XcbAtom::XcbAtom() : m_connection(0), m_reply(0), m_fetched(false) +{ +} + +XcbAtom::XcbAtom(xcb_connection_t *c, const char *name, bool onlyIfExists) + : m_reply(0), m_fetched(false) +{ + intern(c, name, onlyIfExists); +} + +void XcbAtom::intern(xcb_connection_t *c, const char *name, bool onlyIfExists) +{ + m_connection = c; + m_cookie = xcb_intern_atom(c, onlyIfExists, std::strlen(name), name); +} + +XcbAtom::~XcbAtom() +{ + std::free(m_reply); +} + +xcb_atom_t XcbAtom::atom() +{ + if (!m_fetched) { + m_fetched = true; + m_reply = xcb_intern_atom_reply(m_connection, m_cookie, 0); + } + if (m_reply) { + return m_reply->atom; + } else { + return 0; + } +} diff --git a/kcms/touchpadx/src/backends/x11/xlibbackend.h b/kcms/touchpadx/src/backends/x11/xlibbackend.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xlibbackend.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 XLIBBACKEND_H +#define XLIBBACKEND_H + +#include +#include +#include +#include +#include +#include +#include + +#include "touchpadbackend.h" +#include "xlibtouchpad.h" + +#include + +#include "xcbatom.h" +#include "propertyinfo.h" + +class XlibTouchpad; +class XlibNotifications; +class XRecordKeyboardMonitor; + +class XlibBackend : public TouchpadBackend +{ + Q_OBJECT + +public: + static XlibBackend* initialize(QObject *parent = 0); + ~XlibBackend(); + + bool applyConfig(const QVariantHash &) override; + bool getConfig(QVariantHash &) override; + QStringList supportedParameters() const override { + return m_device ? m_device->supportedParameters() : QStringList(); + } + QString errorString() const override { return m_errorString; } + int touchpadCount() const override { return m_device ? 1 : 0; } + + void setTouchpadOff(TouchpadOffState) override; + TouchpadOffState getTouchpadOff() override; + + bool isTouchpadAvailable() override; + bool isTouchpadEnabled() override; + void setTouchpadEnabled(bool) override; + + void watchForEvents(bool keyboard) override; + + QStringList listMouses(const QStringList &blacklist) override; + +private slots: + void propertyChanged(xcb_atom_t); + void touchpadDetached(); + void devicePlugged(int); + +protected: + explicit XlibBackend(QObject *parent); + + struct XDisplayCleanup { + static void cleanup(Display *); + }; + + QScopedPointer m_display; + xcb_connection_t *m_connection; + + XcbAtom m_enabledAtom, m_mouseAtom, m_keyboardAtom, m_touchpadAtom; + XcbAtom m_synapticsIdentifierAtom; + XcbAtom m_libinputIdentifierAtom; + + XlibTouchpad *findTouchpad(); + QScopedPointer m_device; + + QString m_errorString; + QScopedPointer m_notifications; + QScopedPointer m_keyboard; +}; + +#endif // XLIBBACKEND_H diff --git a/kcms/touchpadx/src/backends/x11/xlibbackend.cpp b/kcms/touchpadx/src/backends/x11/xlibbackend.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xlibbackend.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 + +#include +#include + +//Includes are ordered this way because of #defines in Xorg's headers +#include "xrecordkeyboardmonitor.h" // krazy:exclude=includes +#include "xlibbackend.h" // krazy:exclude=includes +#include "xlibnotifications.h" // krazy:exclude=includes +#include "libinputtouchpad.h" +#include "synapticstouchpad.h" +#include "propertyinfo.h" + +#include +#include +#include +#include + +#include +#include + +struct DeviceListDeleter +{ + static void cleanup(XDeviceInfo *p) + { + if (p) { + XFreeDeviceList(p); + } + } +}; + +void XlibBackend::XDisplayCleanup::cleanup(Display *p) +{ + if (p) { + XCloseDisplay(p); + } +} + +XlibBackend* XlibBackend::initialize(QObject *parent) +{ + XlibBackend* backend = new XlibBackend(parent); + if (!backend->m_display) { + delete backend; + return nullptr; + } + return backend; +} + +XlibBackend::~XlibBackend() +{ +} + +XlibBackend::XlibBackend(QObject *parent) : + TouchpadBackend(parent), + m_display(XOpenDisplay(0)), m_connection(0) +{ + if (m_display) { + m_connection = XGetXCBConnection(m_display.data()); + } + + if (!m_connection) { + m_errorString = i18n("Cannot connect to X server"); + return; + } + + m_mouseAtom.intern(m_connection, XI_MOUSE); + m_keyboardAtom.intern(m_connection, XI_KEYBOARD); + m_touchpadAtom.intern(m_connection, XI_TOUCHPAD); + m_enabledAtom.intern(m_connection, XI_PROP_ENABLED); + + m_synapticsIdentifierAtom.intern(m_connection, SYNAPTICS_PROP_CAPABILITIES); + m_libinputIdentifierAtom.intern(m_connection, "libinput Send Events Modes Available"); + + m_device.reset(findTouchpad()); + if (!m_device) { + m_errorString = i18n("No touchpad found"); + } +} + +XlibTouchpad* XlibBackend::findTouchpad() +{ + int nDevices = 0; + QScopedPointer + deviceInfo(XListInputDevices(m_display.data(), &nDevices)); + + for (XDeviceInfo *info = deviceInfo.data(); + info < deviceInfo.data() + nDevices; info++) + { + // Make sure device is touchpad + if (info->type != m_touchpadAtom.atom()) { + continue; + } + int nProperties = 0; + QSharedPointer properties( + XIListProperties(m_display.data(), info->id, + &nProperties), XDeleter); + + Atom *atom = properties.data(), *atomEnd = properties.data() + nProperties; + for (; atom != atomEnd; atom++) { + if (*atom == m_libinputIdentifierAtom.atom()) { + return new LibinputTouchpad(m_display.data(), info->id); + } else if (*atom == m_synapticsIdentifierAtom.atom()) { + return new SynapticsTouchpad(m_display.data(), info->id); + } + } + } + + return nullptr; +} + +bool XlibBackend::applyConfig(const QVariantHash &p) +{ + if (!m_device) { + return false; + } + + bool success = m_device->applyConfig(p); + if (!success) { + m_errorString = i18n("Cannot apply touchpad configuration"); + } + + return success; +} + +bool XlibBackend::getConfig(QVariantHash &p) +{ + if (!m_device) { + return false; + } + + bool success = m_device->getConfig(p); + if (!success) { + m_errorString = i18n("Cannot read touchpad configuration"); + } + return success; +} + +void XlibBackend::setTouchpadEnabled(bool enable) +{ + if (!m_device) { + return; + } + + m_device->setEnabled(enable); + + // FIXME? This should not be needed, m_notifications should trigger + // a propertyChanged signal when we enable/disable the touchpad, + // that will emit touchpadStateChanged, but for some reason + // XlibNotifications is not getting the property change events + // so we just emit touchpadStateChanged from here as a workaround + Q_EMIT touchpadStateChanged(); +} + +void XlibBackend::setTouchpadOff(TouchpadBackend::TouchpadOffState state) +{ + if (!m_device) { + return; + } + + int touchpadOff = 0; + switch (state) { + case TouchpadEnabled: + touchpadOff = 0; + break; + case TouchpadFullyDisabled: + touchpadOff = 1; + break; + case TouchpadTapAndScrollDisabled: + touchpadOff = 2; + break; + default: + qCritical() << "Unknown TouchpadOffState" << state; + return; + } + + m_device->setTouchpadOff(touchpadOff); +} + +bool XlibBackend::isTouchpadAvailable() +{ + return m_device; +} + +bool XlibBackend::isTouchpadEnabled() +{ + if (!m_device) { + return false; + } + + return m_device->enabled(); +} + +TouchpadBackend::TouchpadOffState XlibBackend::getTouchpadOff() +{ + if (!m_device) { + return TouchpadFullyDisabled; + } + int value = m_device->touchpadOff(); + switch (value) { + case 0: + return TouchpadEnabled; + case 1: + return TouchpadFullyDisabled; + case 2: + return TouchpadTapAndScrollDisabled; + default: + qCritical() << "Unknown TouchpadOff value" << value; + return TouchpadFullyDisabled; + } +} + +void XlibBackend::touchpadDetached() +{ + qWarning() << "Touchpad detached"; + m_device.reset(); + Q_EMIT touchpadReset(); +} + +void XlibBackend::devicePlugged(int device) +{ + if (!m_device) { + m_device.reset(findTouchpad()); + if (m_device) { + qWarning() << "Touchpad reset"; + m_notifications.reset(); + watchForEvents(m_keyboard); + Q_EMIT touchpadReset(); + } + } + if (!m_device || device != m_device->deviceId()) { + Q_EMIT mousesChanged(); + } +} + +void XlibBackend::propertyChanged(xcb_atom_t prop) +{ + if ((m_device && prop == m_device->touchpadOffAtom().atom()) || + prop == m_enabledAtom.atom()) + { + Q_EMIT touchpadStateChanged(); + } +} + +QStringList XlibBackend::listMouses(const QStringList &blacklist) +{ + int nDevices = 0; + QScopedPointer + info(XListInputDevices(m_display.data(), &nDevices)); + QStringList list; + for (XDeviceInfo *i = info.data(); i != info.data() + nDevices; i++) { + if (m_device && i->id == static_cast(m_device->deviceId())) { + continue; + } + if (i->use != IsXExtensionPointer && i->use != IsXPointer) { + continue; + } + //type = KEYBOARD && use = Pointer means usb receiver for both keyboard + //and mouse + if (i->type != m_mouseAtom.atom() && i->type != m_keyboardAtom.atom()) { + continue; + } + QString name(i->name); + if (blacklist.contains(name, Qt::CaseInsensitive)) { + continue; + } + PropertyInfo enabled(m_display.data(), i->id, m_enabledAtom.atom(), 0); + if (enabled.value(0) == false) { + continue; + } + list.append(name); + } + + return list; +} + +void XlibBackend::watchForEvents(bool keyboard) +{ + if (!m_notifications) { + m_notifications.reset(new XlibNotifications(m_display.data(), m_device ? m_device->deviceId() : XIAllDevices)); + connect(m_notifications.data(), SIGNAL(devicePlugged(int)), + SLOT(devicePlugged(int))); + connect(m_notifications.data(), SIGNAL(touchpadDetached()), + SLOT(touchpadDetached())); + connect(m_notifications.data(), SIGNAL(propertyChanged(xcb_atom_t)), + SLOT(propertyChanged(xcb_atom_t))); + } + + if (keyboard == !m_keyboard.isNull()) { + return; + } + + if (!keyboard) { + m_keyboard.reset(); + return; + } + + m_keyboard.reset(new XRecordKeyboardMonitor(m_display.data())); + connect(m_keyboard.data(), SIGNAL(keyboardActivityStarted()), + SIGNAL(keyboardActivityStarted())); + connect(m_keyboard.data(), SIGNAL(keyboardActivityFinished()), + SIGNAL(keyboardActivityFinished())); +} diff --git a/kcms/touchpadx/src/backends/x11/xlibnotifications.h b/kcms/touchpadx/src/backends/x11/xlibnotifications.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xlibnotifications.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 XLIBNOTIFICATIONS_H +#define XLIBNOTIFICATIONS_H + +#include +#include +#include + +#include +#include + +class XlibNotifications : public QObject +{ + Q_OBJECT +public: + XlibNotifications(Display *display, int device); + ~XlibNotifications(); + +Q_SIGNALS: + void propertyChanged(xcb_atom_t); + void devicePlugged(int); + void touchpadDetached(); + +private Q_SLOTS: + void processEvents(); + +private: + void processEvent(XEvent *); + + Display *m_display; + xcb_connection_t *m_connection; + QSocketNotifier *m_notifier; + xcb_window_t m_inputWindow; + uint8_t m_inputOpcode; + int m_device; +}; + +#endif // XLIBNOTIFICATIONS_H diff --git a/kcms/touchpadx/src/backends/x11/xlibnotifications.cpp b/kcms/touchpadx/src/backends/x11/xlibnotifications.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xlibnotifications.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "xlibnotifications.h" + +#include + +#include +#include +#include +#include +#include + +XlibNotifications::XlibNotifications(Display *display, int device) + : m_display(display), m_device(device) +{ + m_connection = XGetXCBConnection(display); + + m_notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), + QSocketNotifier::Read, this); + + xcb_query_extension_cookie_t inputExtCookie = + xcb_query_extension(m_connection, std::strlen(INAME), INAME); + QScopedPointer + inputExt(xcb_query_extension_reply(m_connection, inputExtCookie, 0)); + if (!inputExt) { + return; + } + m_inputOpcode = inputExt->major_opcode; + + const xcb_setup_t *setup = xcb_get_setup(m_connection); + xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); + xcb_screen_t *screen = iter.data; + + m_inputWindow = xcb_generate_id(m_connection); + xcb_create_window(m_connection, 0, m_inputWindow, screen->root, 0, 0, 1, 1, + 0, XCB_WINDOW_CLASS_INPUT_ONLY, 0, 0, 0); + xcb_flush(m_connection); + + XIEventMask masks[2]; + + unsigned char touchpadMask[] = { 0, 0, 0, 0 }; + masks[0].deviceid = device; + masks[0].mask = touchpadMask; + masks[0].mask_len = sizeof(touchpadMask); + XISetMask(touchpadMask, XI_PropertyEvent); + + unsigned char allMask[] = { 0, 0, 0, 0 }; + masks[1].deviceid = XIAllDevices; + masks[1].mask = allMask; + masks[1].mask_len = sizeof(allMask); + XISetMask(allMask, XI_HierarchyChanged); + + XISelectEvents(display, XDefaultRootWindow(display), masks, + sizeof(masks) / sizeof(XIEventMask)); + XFlush(display); + + connect(m_notifier, SIGNAL(activated(int)), SLOT(processEvents())); + m_notifier->setEnabled(true); +} + +void XlibNotifications::processEvents() +{ + while (XPending(m_display)) { + XEvent event; + XNextEvent(m_display, &event); + processEvent(&event); + } +} + +struct XEventDataDeleter +{ + XEventDataDeleter(Display *display, XGenericEventCookie *cookie) + : m_display(display), m_cookie(cookie) + { + XGetEventData(m_display, m_cookie); + } + + ~XEventDataDeleter() + { + if (m_cookie->data) { + XFreeEventData(m_display, m_cookie); + } + } + + Display *m_display; + XGenericEventCookie *m_cookie; +}; + +void XlibNotifications::processEvent(XEvent *event) +{ + if (event->xcookie.type != GenericEvent) { + return; + } + if (event->xcookie.extension != m_inputOpcode) { + return; + } + + if (event->xcookie.evtype == XI_PropertyEvent) { + XEventDataDeleter helper(m_display, &event->xcookie); + if (!event->xcookie.data) { + return; + } + + XIPropertyEvent *propEvent = + reinterpret_cast(event->xcookie.data); + Q_EMIT propertyChanged(propEvent->property); + } else if (event->xcookie.evtype == XI_HierarchyChanged) { + XEventDataDeleter helper(m_display, &event->xcookie); + if (!event->xcookie.data) { + return; + } + + XIHierarchyEvent *hierarchyEvent = + reinterpret_cast(event->xcookie.data); + for (uint16_t i = 0; i < hierarchyEvent->num_info; i++) { + if (hierarchyEvent->info[i].deviceid == m_device) { + if (hierarchyEvent->info[i].flags & XISlaveRemoved) { + Q_EMIT touchpadDetached(); + return; + } + } + if (hierarchyEvent->info[i].use != XISlavePointer) { + continue; + } + if (hierarchyEvent->info[i].flags & + (XIDeviceEnabled | XIDeviceDisabled)) + { + Q_EMIT devicePlugged(hierarchyEvent->info[i].deviceid); + } + } + } +} + +XlibNotifications::~XlibNotifications() +{ + xcb_destroy_window(m_connection, m_inputWindow); + xcb_flush(m_connection); +} + +#include "moc_xlibnotifications.cpp" diff --git a/kcms/touchpadx/src/backends/x11/xlibtouchpad.h b/kcms/touchpadx/src/backends/x11/xlibtouchpad.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xlibtouchpad.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 Weng Xuetian + * + * 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 XLIBTOUCHPAD_H +#define XLIBTOUCHPAD_H + +#include +#include + +#include +#include "xcbatom.h" +#include "propertyinfo.h" + +enum ParaType { + PT_INT, + PT_BOOL, + PT_DOUBLE +}; + +struct Parameter { + const char *name; /* Name of parameter */ + enum ParaType type; /* Type of parameter */ + double min_val; /* Minimum allowed value */ + double max_val; /* Maximum allowed value */ + const char *prop_name; /* Property name */ + int prop_format; /* Property format (0 for floats) */ + unsigned prop_offset; /* Offset inside property */ +}; + +class XlibTouchpad +{ +public: + XlibTouchpad(Display *display, int deviceId); + virtual ~XlibTouchpad() {}; + + int deviceId() { return m_deviceId; } + const QStringList &supportedParameters() const { return m_supported; } + bool applyConfig(const QVariantHash &p); + bool getConfig(QVariantHash &p); + void setEnabled(bool enable); + bool enabled(); + void setTouchpadOff(int touchpadOff); + int touchpadOff(); + + XcbAtom &touchpadOffAtom(); + +protected: + void loadSupportedProperties(const Parameter *props); + bool setParameter(const struct Parameter *, const QVariant &); + QVariant getParameter(const struct Parameter *); + struct PropertyInfo *getDevProperty(const QLatin1String &propName); + void flush(); + virtual double getPropertyScale(const QString &name) const; + const Parameter * findParameter(const QString &name); + + Display *m_display; + xcb_connection_t *m_connection; + int m_deviceId; + + XcbAtom m_floatType, m_enabledAtom, m_touchpadOffAtom; + + QMap > m_atoms; + + QMap m_negate; + QMap m_props; + QSet m_changed; + QStringList m_supported; + const struct Parameter *m_paramList; +}; + +#endif diff --git a/kcms/touchpadx/src/backends/x11/xlibtouchpad.cpp b/kcms/touchpadx/src/backends/x11/xlibtouchpad.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xlibtouchpad.cpp @@ -0,0 +1,285 @@ +#include + +#include "xlibtouchpad.h" +#include +#include +#include +#include + +static QVariant negateVariant(const QVariant &value) +{ + if (value.type() == QVariant::Double) { + return QVariant(-value.toDouble()); + } else if (value.type() == QVariant::Int) { + return QVariant(-value.toInt()); + } + return value; +} + +XlibTouchpad::XlibTouchpad(Display *display, int deviceId) : + m_display(display), + m_connection(XGetXCBConnection(display)), + m_deviceId(deviceId) +{ + m_floatType.intern(m_connection, "FLOAT"); + m_enabledAtom.intern(m_connection, XI_PROP_ENABLED); +} + +bool XlibTouchpad::applyConfig(const QVariantHash& p) +{ + m_props.clear(); + + bool error = false; + Q_FOREACH(const QString &name, m_supported) { + QVariantHash::ConstIterator i = p.find(name); + if (i == p.end()) { + continue; + } + const Parameter *par = findParameter(name); + if (par) { + QVariant value(i.value()); + + double k = getPropertyScale(name); + if (k != 1.0) { + bool ok = false; + value = QVariant(value.toDouble(&ok) * k); + if (!ok) { + error = true; + continue; + } + } + + if (m_negate.contains(name)) { + QVariantHash::ConstIterator i = p.find(m_negate[name]); + if (i != p.end() && i.value().toBool()) { + value = negateVariant(value); + } + } + + if (name == "CoastingSpeed") { + QVariantHash::ConstIterator coastingEnabled = p.find("Coasting"); + if (coastingEnabled != p.end() && + !coastingEnabled.value().toBool()) + { + value = QVariant(0); + } + } + + if (!setParameter(par, value)) { + error = true; + } + } + } + + flush(); + + return !error; +} + +bool XlibTouchpad::getConfig(QVariantHash& p) +{ + if (m_supported.isEmpty()) { + return false; + } + + m_props.clear(); + + bool error = false; + Q_FOREACH(const QString &name, m_supported) { + const Parameter *par = findParameter(name); + if (!par) { + continue; + } + + QVariant value(getParameter(par)); + if (!value.isValid()) { + error = true; + continue; + } + + double k = getPropertyScale(name); + if (k != 1.0) { + bool ok = false; + value = QVariant(value.toDouble(&ok) / k); + if (!ok) { + error = true; + continue; + } + } + + if (m_negate.contains(name)) { + bool negative = value.toDouble() < 0.0; + p[m_negate[name]] = QVariant(negative); + if (negative) { + value = negateVariant(value); + } + } + + if (name == "CoastingSpeed") { + bool coasting = value.toDouble() != 0.0; + p["Coasting"] = QVariant(coasting); + if (!coasting) { + continue; + } + } + + p[name] = value; + } + + return !error; +} + +void XlibTouchpad::loadSupportedProperties(const Parameter* props) +{ + m_paramList = props; + for (const Parameter *param = props; param->name; param++) { + QLatin1String name(param->prop_name); + + if (!m_atoms.contains(name)) { + m_atoms.insert(name, QSharedPointer( + new XcbAtom(m_connection, param->prop_name))); + } + } + + for (const Parameter *p = props; p->name; p++) { + if (getParameter(p).isValid()) { + m_supported.append(p->name); + } + } +} + +QVariant XlibTouchpad::getParameter(const Parameter* par) +{ + PropertyInfo *p = getDevProperty(QLatin1String(par->prop_name)); + if (!p || par->prop_offset >= p->nitems) { + return QVariant(); + } + + return p->value(par->prop_offset); +} + + +void XlibTouchpad::flush() +{ + Q_FOREACH(const QLatin1String &name, m_changed) { + m_props[name].set(); + } + m_changed.clear(); + + XFlush(m_display); +} + +double XlibTouchpad::getPropertyScale(const QString& name) const +{ + Q_UNUSED(name); + return 1.0; +} + +PropertyInfo* XlibTouchpad::getDevProperty(const QLatin1String& propName) +{ + if (m_props.contains(propName)) { + return &m_props[propName]; + } + + if (!m_atoms.contains(propName) || !m_atoms[propName]) { + return 0; + } + + xcb_atom_t prop = m_atoms[propName]->atom(); + if (!prop) { + return 0; + } + + PropertyInfo p(m_display, m_deviceId, prop, m_floatType.atom()); + if (!p.b && !p.f && !p.i) { + return 0; + } + return &m_props.insert(propName, p).value(); +} + +bool XlibTouchpad::setParameter(const Parameter *par, const QVariant &value) +{ + QLatin1String propName(par->prop_name); + PropertyInfo *p = getDevProperty(propName); + if (!p || par->prop_offset >= p->nitems) { + return false; + } + + QVariant converted(value); + QVariant::Type convType = QVariant::Int; + if (p->f) { + convType = QVariant::Double; + } else if (value.type() == QVariant::Double) { + converted = QVariant(qRound(static_cast(value.toDouble()))); + } + + if (!converted.convert(convType)) { + return false; + } + + if (converted == p->value(par->prop_offset)) { + return true; + } + + if (p->b) { + p->b[par->prop_offset] = static_cast(converted.toInt()); + } else if (p->i) { + p->i[par->prop_offset] = converted.toInt(); + } else if (p->f) { + p->f[par->prop_offset] = converted.toDouble(); + } + + m_changed.insert(propName); + return true; +} + + +void XlibTouchpad::setEnabled(bool enable) +{ + PropertyInfo enabled(m_display, m_deviceId, m_enabledAtom.atom(), 0); + if (enabled.b && *(enabled.b) != enable) { + *(enabled.b) = enable; + enabled.set(); + } + + flush(); +} + +bool XlibTouchpad::enabled() +{ + PropertyInfo enabled(m_display, m_deviceId, m_enabledAtom.atom(), 0); + return enabled.value(0).toBool(); +} + + +void XlibTouchpad::setTouchpadOff(int touchpadOff) +{ + PropertyInfo off(m_display, m_deviceId, m_touchpadOffAtom.atom(), 0); + if (off.b && *(off.b) != touchpadOff) { + *(off.b) = touchpadOff; + off.set(); + } + + flush(); +} + +int XlibTouchpad::touchpadOff() +{ + PropertyInfo off(m_display, m_deviceId, m_touchpadOffAtom.atom(), 0); + return off.value(0).toInt(); +} + +XcbAtom& XlibTouchpad::touchpadOffAtom() +{ + return m_touchpadOffAtom; +} + +const Parameter* XlibTouchpad::findParameter(const QString& name) +{ + for (const Parameter *par = m_paramList; par->name; par++) { + if (name == par->name) { + return par; + } + } + return 0; +} diff --git a/kcms/touchpadx/src/backends/x11/xrecordkeyboardmonitor.h b/kcms/touchpadx/src/backends/x11/xrecordkeyboardmonitor.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xrecordkeyboardmonitor.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 XRECORDKEYBOARDMONITOR_H +#define XRECORDKEYBOARDMONITOR_H + +#include +#include +#include + +#include +#include + +class XRecordKeyboardMonitor : public QObject +{ + Q_OBJECT + +public: + XRecordKeyboardMonitor(Display *display); + ~XRecordKeyboardMonitor(); + +Q_SIGNALS: + void keyboardActivityStarted(); + void keyboardActivityFinished(); + +private Q_SLOTS: + void processNextReply(); + +private: + void process(xcb_record_enable_context_reply_t *reply); + bool activity() const { return m_keysPressed && !m_modifiersPressed; } + + QSocketNotifier *m_notifier; + xcb_connection_t *m_connection; + xcb_record_context_t m_context; + xcb_record_enable_context_cookie_t m_cookie; + + QVector m_modifier, m_ignore, m_pressed; + int m_modifiersPressed, m_keysPressed; +}; + +#endif // XRECORDKEYBOARDMONITOR_H diff --git a/kcms/touchpadx/src/backends/x11/xrecordkeyboardmonitor.cpp b/kcms/touchpadx/src/backends/x11/xrecordkeyboardmonitor.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/backends/x11/xrecordkeyboardmonitor.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "xrecordkeyboardmonitor.h" + +#include +#include + +#include + +#include +#include + +XRecordKeyboardMonitor::XRecordKeyboardMonitor(Display *display) + : m_connection(xcb_connect(XDisplayString(display), 0)), + m_modifiersPressed(0), m_keysPressed(0) +{ + if (!m_connection) { + return; + } + + xcb_get_modifier_mapping_cookie_t modmapCookie = + xcb_get_modifier_mapping(m_connection); + + m_context = xcb_generate_id(m_connection); + xcb_record_range_t range; + memset(&range, 0, sizeof(range)); + range.device_events.first = XCB_KEY_PRESS; + range.device_events.last = XCB_KEY_RELEASE; + xcb_record_client_spec_t cs = XCB_RECORD_CS_ALL_CLIENTS; + xcb_record_create_context(m_connection, m_context, 0, 1, 1, &cs, &range); + xcb_flush(m_connection); + + QScopedPointer + modmap(xcb_get_modifier_mapping_reply(m_connection, modmapCookie, 0)); + if (!modmap) { + return; + } + + int nModifiers = xcb_get_modifier_mapping_keycodes_length(modmap.data()); + xcb_keycode_t *modifiers = xcb_get_modifier_mapping_keycodes(modmap.data()); + m_modifier.fill(false, std::numeric_limits::max() + 1); + for (xcb_keycode_t *i = modifiers; i < modifiers + nModifiers; i++) { + m_modifier[*i] = true; + } + m_ignore.fill(false, std::numeric_limits::max() + 1); + for (xcb_keycode_t *i = modifiers; + i < modifiers + modmap->keycodes_per_modifier; i++) + { + m_ignore[*i] = true; + } + m_pressed.fill(false, std::numeric_limits::max() + 1); + + m_cookie = xcb_record_enable_context(m_connection, m_context); + xcb_flush(m_connection); + + m_notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), + QSocketNotifier::Read, this); + connect(m_notifier, SIGNAL(activated(int)), SLOT(processNextReply())); + m_notifier->setEnabled(true); +} + +XRecordKeyboardMonitor::~XRecordKeyboardMonitor() +{ + if (!m_connection) { + return; + } + + xcb_record_disable_context(m_connection, m_context); + xcb_record_free_context(m_connection, m_context); + xcb_disconnect(m_connection); +} + +void XRecordKeyboardMonitor::processNextReply() +{ + xcb_generic_event_t *event; + while ((event = xcb_poll_for_event(m_connection))) { + std::free(event); + } + + void *reply = 0; + xcb_generic_error_t *error = nullptr; + while (m_cookie.sequence && xcb_poll_for_reply(m_connection, m_cookie.sequence, &reply, &error)) { + // xcb_poll_for_reply may set both reply and error to null if connection has error. + // break if xcb_connection has error, no point to continue anyway. + if (xcb_connection_has_error(m_connection)) { + break; + } + + if (error) { + std::free(error); + break; + } + + if (!reply) { + continue; + } + + QScopedPointer + data(reinterpret_cast(reply)); + process(data.data()); + } +} + +void XRecordKeyboardMonitor::process(xcb_record_enable_context_reply_t *reply) +{ + bool prevActivity = activity(); + + xcb_key_press_event_t *events = reinterpret_cast + (xcb_record_enable_context_data(reply)); + int nEvents = xcb_record_enable_context_data_length(reply) / + sizeof(xcb_key_press_event_t); + bool wasActivity = prevActivity; + for (xcb_key_press_event_t *e = events; e < events + nEvents; e++) { + if (e->response_type != XCB_KEY_PRESS && + e->response_type != XCB_KEY_RELEASE) + { + continue; + } + + if (m_ignore[e->detail]) { + continue; + } + + bool pressed = (e->response_type == XCB_KEY_PRESS); + if (m_pressed[e->detail] == pressed) { + continue; + } + m_pressed[e->detail] = pressed; + + int &counter = m_modifier[e->detail] ? m_modifiersPressed : + m_keysPressed; + if (pressed) { + counter++; + } else { + counter--; + } + + wasActivity = wasActivity || activity(); + } + + if (!prevActivity && activity()) { + Q_EMIT keyboardActivityStarted(); + } else if (!activity() && wasActivity) { + Q_EMIT keyboardActivityFinished(); + } +} diff --git a/kcms/touchpad/src/kcm/kcm_touchpad.desktop b/kcms/touchpadx/src/kcm/kcm_touchpadx.desktop rename from kcms/touchpad/src/kcm/kcm_touchpad.desktop rename to kcms/touchpadx/src/kcm/kcm_touchpadx.desktop --- a/kcms/touchpad/src/kcm/kcm_touchpad.desktop +++ b/kcms/touchpadx/src/kcm/kcm_touchpadx.desktop @@ -1,5 +1,5 @@ [Desktop Entry] -Exec=kcmshell5 kcm_touchpad +Exec=kcmshell5 kcm_touchpadx Icon=input-touchpad Type=Service Categories=Qt;KDE;X-KDE-settings-hardware; @@ -12,6 +12,7 @@ X-KDE-PluginKeyword=kcm X-KDE-ParentApp=kcontrol X-KDE-System-Settings-Parent-Category=input-devices +X-KDE-OnlyShowOnQtPlatforms=xcb Name=Touchpad Name[ar]=لوحة اللمس diff --git a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.h b/kcms/touchpadx/src/kcm/touchpadconfigcontainer.h rename from kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.h rename to kcms/touchpadx/src/kcm/touchpadconfigcontainer.h --- a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.h +++ b/kcms/touchpadx/src/kcm/touchpadconfigcontainer.h @@ -16,46 +16,41 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef TOUCHPADCONFIGLIBINPUT_H -#define TOUCHPADCONFIGLIBINPUT_H +#ifndef TOUCHPADCONFIGCONTAINER_H +#define TOUCHPADCONFIGCONTAINER_H -#include "../touchpadconfigplugin.h" +#include -class TouchpadBackend; -class QHideEvent; -class QQuickWidget; -class KMessageWidget; +class TouchpadConfigPlugin; +class TouchpadConfigXlib; -class TouchpadConfigLibinput : public TouchpadConfigPlugin +class TouchpadConfigContainer : public KCModule { Q_OBJECT + friend TouchpadConfigXlib; + public: - explicit TouchpadConfigLibinput(TouchpadConfigContainer *parent, + explicit TouchpadConfigContainer(QWidget *parent, const QVariantList &args = QVariantList()); - virtual ~TouchpadConfigLibinput() {} + + QSize minimumSizeHint() const override; + QSize sizeHint() const override; + void resizeEvent(QResizeEvent *event) override; void load() override; void save() override; void defaults() override; - QSize sizeHint() const override; - QSize minimumSizeHint() const override; - - void hideEvent(QHideEvent *) override {} + void kcmLoad() {KCModule::load();} + void kcmSave() {KCModule::save();} + void kcmDefaults() {KCModule::defaults();} -private Q_SLOTS: - void onChange(); - void onTouchpadAdded(bool success); - void onTouchpadRemoved(int index); +protected: + void hideEvent(QHideEvent *) override; private: - void hideErrorMessage(); - - QQuickWidget *m_view; - KMessageWidget *m_errorMessage; - - bool m_initError; + TouchpadConfigPlugin* m_plugin = nullptr; }; -#endif // TOUCHPADCONFIGLIBINPUT_H +#endif // TOUCHPADCONFIGCONTAINER_H diff --git a/kcms/touchpad/src/kcm/touchpadconfigcontainer.cpp b/kcms/touchpadx/src/kcm/touchpadconfigcontainer.cpp copy from kcms/touchpad/src/kcm/touchpadconfigcontainer.cpp copy to kcms/touchpadx/src/kcm/touchpadconfigcontainer.cpp --- a/kcms/touchpad/src/kcm/touchpadconfigcontainer.cpp +++ b/kcms/touchpadx/src/kcm/touchpadconfigcontainer.cpp @@ -18,7 +18,6 @@ #include "touchpadconfigcontainer.h" #include "touchpadconfigplugin.h" -#include "kcm/libinput/touchpadconfiglibinput.h" #include "kcm/xlib/touchpadconfigxlib.h" #include @@ -38,8 +37,6 @@ { if (KWindowSystem::isPlatformX11()) { m_plugin = new TouchpadConfigXlib(this); - } else if (KWindowSystem::isPlatformWayland()) { - m_plugin = new TouchpadConfigLibinput(this); } } diff --git a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.h b/kcms/touchpadx/src/kcm/touchpadconfigplugin.h rename from kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.h rename to kcms/touchpadx/src/kcm/touchpadconfigplugin.h --- a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.h +++ b/kcms/touchpadx/src/kcm/touchpadconfigplugin.h @@ -16,46 +16,31 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef TOUCHPADCONFIGLIBINPUT_H -#define TOUCHPADCONFIGLIBINPUT_H +#ifndef TOUCHPADCONFIGPLUGIN_H +#define TOUCHPADCONFIGPLUGIN_H -#include "../touchpadconfigplugin.h" +#include +class TouchpadConfigContainer; class TouchpadBackend; -class QHideEvent; -class QQuickWidget; -class KMessageWidget; -class TouchpadConfigLibinput : public TouchpadConfigPlugin +class TouchpadConfigPlugin : public QWidget { Q_OBJECT public: - explicit TouchpadConfigLibinput(TouchpadConfigContainer *parent, - const QVariantList &args = QVariantList()); - virtual ~TouchpadConfigLibinput() {} + explicit TouchpadConfigPlugin(QWidget *parent); + virtual ~TouchpadConfigPlugin() {} - void load() override; - void save() override; - void defaults() override; - - QSize sizeHint() const override; - QSize minimumSizeHint() const override; + virtual void load() {} + virtual void save() {} + virtual void defaults() {} void hideEvent(QHideEvent *) override {} -private Q_SLOTS: - void onChange(); - void onTouchpadAdded(bool success); - void onTouchpadRemoved(int index); - -private: - void hideErrorMessage(); - - QQuickWidget *m_view; - KMessageWidget *m_errorMessage; - - bool m_initError; +protected: + TouchpadConfigContainer *m_parent; + TouchpadBackend *m_backend; }; -#endif // TOUCHPADCONFIGLIBINPUT_H +#endif // TOUCHPADCONFIGPLUGIN_H diff --git a/kcms/touchpadx/src/kcm/touchpadconfigplugin.cpp b/kcms/touchpadx/src/kcm/touchpadconfigplugin.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/touchpadconfigplugin.cpp @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#include "touchpadconfigplugin.h" +#include "touchpadconfigcontainer.h" + +TouchpadConfigPlugin::TouchpadConfigPlugin(QWidget *parent) + : QWidget(parent), + m_parent(dynamic_cast(parent)) +{ +} diff --git a/kcms/touchpadx/src/kcm/xlib/customconfigdialogmanager.h b/kcms/touchpadx/src/kcm/xlib/customconfigdialogmanager.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/customconfigdialogmanager.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 CUSTOMCONFIGDIALOGMANAGER_H +#define CUSTOMCONFIGDIALOGMANAGER_H + +#include +#include + +#include + +class CustomConfigDialogManager : public KConfigDialogManager +{ + Q_OBJECT +public: + CustomConfigDialogManager(QWidget *parent, + KCoreConfigSkeleton *config, + const QStringList &supported); + ~CustomConfigDialogManager(); + + QVariantHash currentWidgetProperties() const; + void setWidgetProperties(const QVariantHash &); + bool compareWidgetProperties(const QVariantHash &) const; + bool hasChangedFuzzy() const; + +private: + QVariant fixup(QWidget *widget, QVariant value) const; + + QMap m_widgets; + KCoreConfigSkeleton *m_config; +}; + +#endif // CUSTOMCONFIGDIALOGMANAGER_H diff --git a/kcms/touchpadx/src/kcm/xlib/customconfigdialogmanager.cpp b/kcms/touchpadx/src/kcm/xlib/customconfigdialogmanager.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/customconfigdialogmanager.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "customconfigdialogmanager.h" + +#include + +#include +#include + +#include +#include +#include + +#include "customslider.h" + +CustomConfigDialogManager::CustomConfigDialogManager(QWidget *parent, + KCoreConfigSkeleton *conf, + const QStringList &supported) + : KConfigDialogManager(parent, conf), m_config(conf) +{ + static const QString kcfgPrefix("kcfg_"); + + Q_FOREACH(KConfigSkeletonItem *i, conf->items()) { + QString name(i->name()); + + QWidget *child = parent->findChild(kcfgPrefix + name); + if (!child) { + continue; + } + m_widgets[name] = child; + + /* FIXME: this should probably be less hackish */ + if (name == "Tapping" && + !supported.contains("Tapping")) + qobject_cast(child)->setCheckable(false); + else if (!supported.contains(name)) { + child->setEnabled(false); + } + + KCoreConfigSkeleton::ItemEnum *asEnum = + dynamic_cast(i); + if (!asEnum) { + continue; + } + + QStringList choiceList; + Q_FOREACH(const KCoreConfigSkeleton::ItemEnum::Choice &c, + asEnum->choices()) + { + if (c.label.isEmpty()) { + choiceList.append(c.name); + } else { + choiceList.append(c.label); + } + } + + KComboBox *asComboBox = qobject_cast(child); + if (asComboBox) { + asComboBox->addItems(choiceList); + } + } +} + +CustomConfigDialogManager::~CustomConfigDialogManager() +{ +} + +QVariantHash CustomConfigDialogManager::currentWidgetProperties() const +{ + QVariantHash r; + for (QMap::ConstIterator i = m_widgets.begin(); + i != m_widgets.end(); ++i) + { + r[i.key()] = property(i.value()); + } + return r; +} + +void CustomConfigDialogManager::setWidgetProperties(const QVariantHash &p) +{ + for (QVariantHash::ConstIterator i = p.begin(); i != p.end(); ++i) { + QMap::ConstIterator j = m_widgets.constFind(i.key()); + if (j != m_widgets.constEnd()) { + setProperty(j.value(), i.value()); + } + } +} + +static bool variantFuzzyCompare(const QVariant &a, const QVariant &b) +{ + if (a == b) { + return true; + } + + bool isDouble_a = false, isDouble_b = false; + float d_a = static_cast(a.toDouble(&isDouble_a)), + d_b = static_cast(b.toDouble(&isDouble_b)); + if (!isDouble_a || !isDouble_b) { + return false; + } + + return (qFuzzyIsNull(d_a) && qFuzzyIsNull(d_b)) || qFuzzyCompare(d_a, d_b); +} + +QVariant CustomConfigDialogManager::fixup(QWidget *widget, QVariant v) const +{ + bool isDouble = false; + double value = v.toDouble(&isDouble); + if (!isDouble) { + return v; + } + + QVariant decimals(widget->property("decimals")); + if (decimals.type() != QVariant::Int) { + CustomSlider *asSlider = qobject_cast(widget); + if (asSlider) { + return asSlider->fixup(value); + } + return value; + } + + double k = std::pow(10.0, decimals.toInt()); + return std::floor(value * k + 0.5) / k; //round +} + +bool CustomConfigDialogManager::compareWidgetProperties(const QVariantHash &p) const +{ + bool result = true; + for (QVariantHash::ConstIterator i = p.begin(); i != p.end(); ++i) { + QMap::ConstIterator j = m_widgets.find(i.key()); + if (j == m_widgets.end()) { + continue; + } + + QWidget *widget = j.value(); + QVariant widgetValue(fixup(widget, property(widget))); + QVariant fixed(fixup(widget, i.value())); + if (!variantFuzzyCompare(widgetValue, fixed)) { + result = false; + qDebug() << "Config mismatch:" + << widget->objectName() << widgetValue << fixed; + } + } + return result; +} + +bool CustomConfigDialogManager::hasChangedFuzzy() const +{ + for (QMap::ConstIterator i = m_widgets.begin(); + i != m_widgets.end(); ++i) + { + KConfigSkeletonItem *config = m_config->findItem(i.key()); + QWidget *widget = i.value(); + QVariant widgetValue(fixup(widget, property(widget))); + QVariant configValue(fixup(widget, config->property())); + if (!variantFuzzyCompare(widgetValue, configValue)) { + return true; + } + } + return false; +} diff --git a/kcms/touchpadx/src/kcm/xlib/customslider.h b/kcms/touchpadx/src/kcm/xlib/customslider.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/customslider.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 CUSTOMSLIDER_H +#define CUSTOMSLIDER_H + +#include + +class CustomSlider : public QSlider +{ + Q_OBJECT + + Q_PROPERTY(double minimum READ doubleMinimum WRITE setDoubleMinimum) + Q_PROPERTY(double maximum READ doubleMaximum WRITE setDoubleMaximum) + Q_PROPERTY(double value READ doubleValue WRITE setDoubleValue \ + NOTIFY valueChanged USER true) + +public: + explicit CustomSlider(QWidget *parent = 0); + + void setDoubleMinimum(double); + double doubleMinimum() const; + + void setDoubleMaximum(double); + double doubleMaximum() const; + + class Interpolator + { + public: + Interpolator() { } + virtual double absolute(double relative, + double minimum, double maximum) const; + virtual double relative(double absolute, + double minimum, double maximum) const; + + virtual ~Interpolator(); + }; + + class SqrtInterpolator : public Interpolator + { + public: + SqrtInterpolator() { } + double absolute(double relative, double minimum, double maximum) const override; + double relative(double absolute, double minimum, double maximum) const override; + }; + + const Interpolator *interpolator() const; + void setInterpolator(const Interpolator *); + + double doubleValue() const; + double fixup(double) const; + +public Q_SLOTS: + void setDoubleValue(double); + +Q_SIGNALS: + void valueChanged(double); + +protected: + void resizeEvent(QResizeEvent *) override; + +private Q_SLOTS: + void updateValue(); + +private: + void updateRange(const QSize &); + void moveSlider(); + int doubleToInt(double) const; + double intToDouble(int) const; + + static const Interpolator lerp; + + double m_min, m_max, m_value; + const Interpolator *m_interpolator; +}; + +#endif // CUSTOMSLIDER_H diff --git a/kcms/touchpadx/src/kcm/xlib/customslider.cpp b/kcms/touchpadx/src/kcm/xlib/customslider.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/customslider.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "customslider.h" + +#include + +#include + +CustomSlider::Interpolator::~Interpolator() +{ +} + +double CustomSlider::Interpolator::absolute(double relative, + double minimum, + double maximum) const +{ + return relative * (maximum - minimum) + minimum; +} + +double CustomSlider::Interpolator::relative(double absolute, + double minimum, + double maximum) const +{ + return (absolute - minimum) / (maximum - minimum); +} + +double CustomSlider::SqrtInterpolator::absolute( + double relative, double minimum, double maximum) const +{ + relative *= relative; + return Interpolator::absolute(relative, minimum, maximum); +} + +double CustomSlider::SqrtInterpolator::relative( + double absolute, double minimum, double maximum) const +{ + double value = Interpolator::relative(absolute, minimum, maximum); + return std::sqrt(value); +} + +const CustomSlider::Interpolator CustomSlider::lerp; + +CustomSlider::CustomSlider(QWidget *parent) : + QSlider(parent), m_min(0.0), m_max(1.0), m_interpolator(&lerp) +{ + setSingleStep(10); + setPageStep(100); + + updateValue(); + updateRange(size()); + + connect(this, SIGNAL(actionTriggered(int)), SLOT(updateValue())); +} + +void CustomSlider::resizeEvent(QResizeEvent *e) +{ + QSlider::resizeEvent(e); + updateRange(e->size()); +} + +const CustomSlider::Interpolator *CustomSlider::interpolator() const +{ + return m_interpolator; +} + +void CustomSlider::setInterpolator(const CustomSlider::Interpolator *v) +{ + m_interpolator = v; +} + +void CustomSlider::setDoubleMinimum(double v) +{ + m_min = v; +} + +double CustomSlider::doubleMinimum() const +{ + return m_min; +} + +void CustomSlider::setDoubleMaximum(double v) +{ + m_max = v; +} + +double CustomSlider::doubleMaximum() const +{ + return m_max; +} + +double CustomSlider::doubleValue() const +{ + return qBound(m_min, m_value, m_max); +} + +void CustomSlider::setDoubleValue(double v) +{ + if (m_value == v) { + return; + } + + m_value = v; + int oldIntValue = value(); + moveSlider(); + if (value() != oldIntValue) { + Q_EMIT valueChanged(doubleValue()); + } +} + +double CustomSlider::intToDouble(int v) const +{ + double relative = lerp.relative(v, minimum(), maximum()); + return m_interpolator->absolute(relative, m_min, m_max); +} + +void CustomSlider::updateValue() +{ + m_value = intToDouble(sliderPosition()); + Q_EMIT valueChanged(doubleValue()); +} + +double CustomSlider::fixup(double v) const +{ + return intToDouble(doubleToInt(v)); +} + +int CustomSlider::doubleToInt(double v) const +{ + double relative = m_interpolator->relative(v, m_min, m_max); + double absolute = lerp.absolute(relative, minimum(), maximum()); + return static_cast(std::floor(absolute + 0.5)); +} + +void CustomSlider::moveSlider() +{ + setValue(doubleToInt(doubleValue())); +} + +void CustomSlider::updateRange(const QSize &s) +{ + setRange(0, (orientation() == Qt::Horizontal) ? s.width() : s.height()); + moveSlider(); +} diff --git a/kcms/touchpadx/src/kcm/xlib/sliderpair.h b/kcms/touchpadx/src/kcm/xlib/sliderpair.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/sliderpair.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 SLIDERPAIR_H +#define SLIDERPAIR_H + +#include + +class QSlider; + +class SliderPair : public QObject +{ + Q_OBJECT +public: + SliderPair(QSlider *minSlider, QSlider *maxSlider, QObject *parent = 0); + +private Q_SLOTS: + void adjustMinSlider(); + void adjustMaxSlider(); + +private: + QSlider *m_minSlider, *m_maxSlider; +}; + +#endif // SLIDERPAIR_H diff --git a/kcms/touchpadx/src/kcm/xlib/sliderpair.cpp b/kcms/touchpadx/src/kcm/xlib/sliderpair.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/sliderpair.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "sliderpair.h" + +#include + +SliderPair::SliderPair(QSlider *minSlider, QSlider *maxSlider, QObject *parent) + : QObject(parent), m_minSlider(minSlider), m_maxSlider(maxSlider) +{ + connect(m_minSlider, SIGNAL(valueChanged(int)), SLOT(adjustMaxSlider())); + connect(m_maxSlider, SIGNAL(valueChanged(int)), SLOT(adjustMinSlider())); +} + +void SliderPair::adjustMaxSlider() +{ + m_maxSlider->setValue(qMax(m_maxSlider->value(), m_minSlider->value())); +} + +void SliderPair::adjustMinSlider() +{ + m_minSlider->setValue(qMin(m_maxSlider->value(), m_minSlider->value())); +} diff --git a/kcms/touchpadx/src/kcm/xlib/testarea.h b/kcms/touchpadx/src/kcm/xlib/testarea.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/testarea.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 TESTAREA_H +#define TESTAREA_H + +#include "ui_testarea.h" + +class TestArea : public QWidget +{ + Q_OBJECT +public: + explicit TestArea(QWidget *); + +Q_SIGNALS: + void enter(); + void leave(); + +protected: + void enterEvent(QEvent *) override; + void leaveEvent(QEvent *) override; + +private: + Ui::TestArea m_ui; +}; + +#endif // TESTAREA_H diff --git a/kcms/touchpadx/src/kcm/xlib/testarea.cpp b/kcms/touchpadx/src/kcm/xlib/testarea.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/testarea.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "testarea.h" + +#include +#include + +#include +#include + +TestArea::TestArea(QWidget *parent) : QWidget(parent) +{ + m_ui.setupUi(this); + + m_ui.listWidget->addItem(new QListWidgetItem(QIcon::fromTheme("folder"), + i18n("Drag me"), m_ui.listWidget)); + + Plasma::Theme defaultTheme; + QString wallpaper = defaultTheme.wallpaperPath(); + static const QString stylesheet("background-image: url(%1)"); + m_ui.scrollAreaWidgetContents->setStyleSheet(stylesheet.arg(wallpaper)); +} + +void TestArea::enterEvent(QEvent *e) +{ + Q_EMIT enter(); + QWidget::enterEvent(e); +} + +void TestArea::leaveEvent(QEvent *e) +{ + Q_EMIT leave(); + QWidget::leaveEvent(e); +} diff --git a/kcms/touchpadx/src/kcm/xlib/testbutton.h b/kcms/touchpadx/src/kcm/xlib/testbutton.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/testbutton.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 TESTBUTTON_H +#define TESTBUTTON_H + +#include + +class TestButton : public QPushButton +{ + Q_OBJECT + +public: + explicit TestButton(QWidget *); + +protected: + void mousePressEvent(QMouseEvent *) override; + +private Q_SLOTS: + void resetText(); + +private: + QString m_originalText; + bool m_firstClick; +}; + +#endif // TESTBUTTON_H diff --git a/kcms/touchpadx/src/kcm/xlib/testbutton.cpp b/kcms/touchpadx/src/kcm/xlib/testbutton.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/testbutton.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "testbutton.h" + +#include +#include + +#include + +TestButton::TestButton(QWidget *parent) + : QPushButton(parent), m_firstClick(true) +{ +} + +void TestButton::mousePressEvent(QMouseEvent *e) +{ + if (m_firstClick) { + m_originalText = text(); + m_firstClick = false; + } + + switch (e->button()) { + case Qt::LeftButton: + setText(i18nc("Mouse button", "Left button")); + break; + case Qt::RightButton: + setText(i18nc("Mouse button", "Right button")); + break; + case Qt::MiddleButton: + setText(i18nc("Mouse button", "Middle button")); + break; + default: + break; + } + + QTimer::singleShot(500, this, SLOT(resetText())); + + QPushButton::mousePressEvent(e); +} + +void TestButton::resetText() +{ + setText(m_originalText); +} diff --git a/kcms/touchpadx/src/kcm/xlib/touchpad.kcfg b/kcms/touchpadx/src/kcm/xlib/touchpad.kcfg new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/touchpad.kcfg @@ -0,0 +1,317 @@ + + + + + + + systemDefault("Tapping", true) + + + + + + OneFingerTap + TwoFingerTap + ThreeFingerTap + LTCorner + LBCorner + RTCorner + RBCorner + + + + + + + + + + + + + + + + + + systemDefaultEnum("OneFingerTapButton", LeftButton) + + + systemDefaultEnum("TwoFingerTapButton", RightButton) + + + systemDefaultEnum("ThreeFingerTapButton", MiddleButton) + + + systemDefaultEnum("LTCornerButton", NoButton) + + + systemDefaultEnum("LBCornerButton", NoButton) + + + systemDefaultEnum("RTCornerButton", NoButton) + + + systemDefaultEnum("RBCornerButton", NoButton) + + + + + systemDefault("VertEdgeScroll", true) + + + + + systemDefault("VertTwoFingerScroll", true) + + + + + systemDefault("HorizEdgeScroll", true) + + + + + systemDefault("HorizTwoFingerScroll", true) + + + + 0 + 255 + + systemDefault("MinSpeed", 0.0) + + + + 0 + 255 + + systemDefault("MaxSpeed", 255.0) + + + + 0 + 1 + + systemDefault("AccelFactor", 0.0) + + + + 0 + 100 + + systemDefault("HorizHysteresis", 0) + + + + 0 + 100 + + systemDefault("VertHysteresis", 0) + + + + + systemDefault("TapAndDragGesture", true) + + + + + systemDefault("LockedDrags", false) + + + + 0 + 30000 + + systemDefault("LockedDragTimeout", 5000) + + + + 0 + 1000 + + systemDefault("VertScrollDelta", 1.0) + + + + + systemDefault("InvertVertScroll", false) + + + + 0 + 1000 + + systemDefault("HorizScrollDelta", 1.0) + + + + + systemDefault("InvertHorizScroll", false) + + + + + systemDefault("CornerCoasting", false) + + + + 0.1 + 255 + + systemDefault("CoastingSpeed", 20.0) + + + + 0 + 255 + + systemDefault("CoastingFriction", 50.0) + + + + + systemDefault("Coasting", true) + + + + 0 + 255 + + systemDefault("FingerHigh", 30) + + + + 0 + 255 + + systemDefault("FingerLow", 25) + + + + + systemDefault("PalmDetect", false) + + + + 0 + 255 + + systemDefault("PalmMinZ", 200) + + + + 0 + 15 + + systemDefault("PalmMinWidth", 10) + + + + 0 + 1000 + + systemDefault("MaxTapTime", 180) + + + + 0 + 1000 + + systemDefault("MaxDoubleTapTime", 180) + + + + 0 + 1000 + + systemDefault("SingleTapTimeout", 180) + + + + 0 + 1000 + + systemDefault("MaxTapMove", 2.0) + + + + 1 + 255 + + systemDefault("PressureMotionMinZ", 30) + + + + 1 + 255 + + systemDefault("PressureMotionMaxZ", 160) + + + + 0 + 10 + + systemDefault("PressureMotionMinFactor", 1.0) + + + + 0 + 10 + + systemDefault("PressureMotionMaxFactor", 1.0) + + + + + systemDefault("CircularScrolling", false) + + + + + systemDefault("CircScrollDelta", 10.0) + + 0 + 45 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + systemDefaultEnum("CircScrollTrigger", AllEdges) + + + + diff --git a/kcms/touchpadx/src/kcm/xlib/touchpadconfigxlib.h b/kcms/touchpadx/src/kcm/xlib/touchpadconfigxlib.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/touchpadconfigxlib.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 TOUCHPADCONFIGXLIB_H +#define TOUCHPADCONFIGXLIB_H + +#include "../touchpadconfigplugin.h" + +#include +#include + +#include "touchpadparameters.h" +#include "testarea.h" +#include "kdedsettings.h" + +#include "ui_pointermotion.h" +#include "ui_tap.h" +#include "ui_scroll.h" +#include "ui_sensitivity.h" +#include "ui_kded.h" + +class TouchpadConfigContainer; +class TouchpadBackend; +class KMessageWidget; +class OrgKdeTouchpadInterface; +class CustomConfigDialogManager; +class QAction; +class KShortcutsDialog; +class QTabWidget; +class KComboBox; +class QDBusPendingCallWatcher; +class QHideEvent; + +class TouchpadConfigXlib : public TouchpadConfigPlugin +{ + Q_OBJECT + +public: + explicit TouchpadConfigXlib(TouchpadConfigContainer *parent, + const QVariantList &args = QVariantList()); + ~TouchpadConfigXlib() override; + + static void kcmInit(); + + void load() override; + void save() override; + void defaults() override; + + void hideEvent(QHideEvent *) override; + +private Q_SLOTS: + void beginTesting(); + void endTesting(); + void onChanged(); + void checkChanges(); + void loadActiveConfig(); + void updateTestAreaEnabled(); + void updateMouseList(); + void showConfigureNotificationsDialog(); + void gotReplyFromDaemon(QDBusPendingCallWatcher *); + +private: + QVariantHash getActiveConfig(); + + TouchpadParameters m_config; + + QScopedPointer m_prevConfig; + CustomConfigDialogManager *m_manager; + TouchpadDisablerSettings m_daemonSettings; + KConfigDialogManager *m_daemonConfigManager; + KMessageWidget *m_errorMessage, *m_configOutOfSyncMessage; + TestArea *m_testArea; + OrgKdeTouchpadInterface *m_daemon; + QAction *m_loadActiveConfiguration; + bool m_configOutOfSync; + QScopedPointer m_shortcutsDialog; + QWidget *m_kdedTab; + QTabWidget *m_tabs; + KComboBox *m_mouseCombo; + + Ui::PointerMotionForm m_pointerMotion; + Ui::TapForm m_tapping; + Ui::ScrollForm m_scrolling; + Ui::SensitivityForm m_sensitivity; + Ui::KdedForm m_kded; +}; + +#endif // TOUCHPADCONFIGXLIB_H diff --git a/kcms/touchpad/src/kcm/xlib/touchpadconfigxlib.cpp b/kcms/touchpadx/src/kcm/xlib/touchpadconfigxlib.cpp copy from kcms/touchpad/src/kcm/xlib/touchpadconfigxlib.cpp copy to kcms/touchpadx/src/kcm/xlib/touchpadconfigxlib.cpp --- a/kcms/touchpad/src/kcm/xlib/touchpadconfigxlib.cpp +++ b/kcms/touchpadx/src/kcm/xlib/touchpadconfigxlib.cpp @@ -102,8 +102,8 @@ : TouchpadConfigPlugin(parent), m_configOutOfSync(false) { - KAboutData* data = new KAboutData(QStringLiteral("kcm_touchpad"), - i18n("Touchpad KCM"), + KAboutData* data = new KAboutData(QStringLiteral("kcm_touchpadx"), + i18n("TouchpadX KCM"), TOUCHPAD_KCM_VERSION, i18n("System Settings module, daemon and Plasma applet for managing your touchpad"), KAboutLicense::GPL_V2, diff --git a/kcms/touchpadx/src/kcm/xlib/touchpadparameters.kcfgc b/kcms/touchpadx/src/kcm/xlib/touchpadparameters.kcfgc new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/touchpadparameters.kcfgc @@ -0,0 +1,10 @@ +File=touchpad.kcfg +ClassName=TouchpadParameters +Mutators=true +MemberVariables=private +GlobalEnums=true +UseEnumTypes=true +Inherits=TouchpadParametersBase +IncludeFiles=\"kcm/xlib/touchpadparametersbase.h\" +SetUserTexts=true +TranslationSystem=kde diff --git a/kcms/touchpadx/src/kcm/xlib/touchpadparametersbase.h b/kcms/touchpadx/src/kcm/xlib/touchpadparametersbase.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/touchpadparametersbase.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 TOUCHPADPARAMETERSBASE_H +#define TOUCHPADPARAMETERSBASE_H + +#include + +#include + +class TouchpadParametersBase : public KCoreConfigSkeleton +{ +public: + explicit TouchpadParametersBase(const QString &configname = QString(), + QObject *parent = 0); + + QVariantHash values() const; + void setValues(const QVariantHash &); + + static void setSystemDefaults(); + static QVariant systemDefault(const QString &name, + const QVariant &hardcoded = QVariant()); + template + static T systemDefault(const QString &name, const T &hardcoded = T()) + { + return qvariant_cast(systemDefault(name, QVariant(hardcoded))); + } + + template + static T systemDefaultEnum(const QString &name, const T &hardcoded = T()) + { + return static_cast(systemDefault(name, static_cast(hardcoded))); + } +}; + +#endif // TOUCHPADPARAMETERSBASE_H diff --git a/kcms/touchpadx/src/kcm/xlib/touchpadparametersbase.cpp b/kcms/touchpadx/src/kcm/xlib/touchpadparametersbase.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/touchpadparametersbase.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "touchpadparametersbase.h" +#include "touchpadbackend.h" + +namespace +{ + +KConfigGroup &systemDefaults() +{ + static KSharedConfig::Ptr p(KSharedConfig::openConfig(".touchpaddefaults", + KConfig::SimpleConfig, + QStandardPaths::TempLocation)); + static KConfigGroup group(p->group("parameters")); + return group; +} + +} + +TouchpadParametersBase::TouchpadParametersBase(const QString &configname, + QObject *parent) + : KCoreConfigSkeleton(configname, parent) +{ + if (!systemDefaults().exists()) { + setSystemDefaults(); + } +} + +QVariantHash TouchpadParametersBase::values() const +{ + QVariantHash r; + Q_FOREACH(const KConfigSkeletonItem *i, items()) { + r[i->name()] = i->property(); + } + return r; +} + +void TouchpadParametersBase::setValues(const QVariantHash &v) +{ + for (QVariantHash::ConstIterator i = v.begin(); i != v.end(); ++i) { + KConfigSkeletonItem *j = findItem(i.key()); + if (j) { + j->setProperty(i.value()); + } + } +} + +void TouchpadParametersBase::setSystemDefaults() +{ + TouchpadBackend *backend = TouchpadBackend::implementation(); + if (!backend) { + return; + } + QVariantHash v; + backend->getConfig(v); + + for (QVariantHash::ConstIterator i = v.constBegin(); i != v.constEnd(); ++i) { + systemDefaults().writeEntry(i.key(), i.value()); + } + systemDefaults().sync(); +} + +QVariant TouchpadParametersBase::systemDefault(const QString &name, + const QVariant &hardcoded) +{ + return systemDefaults().readEntry(name, hardcoded); +} diff --git a/kcms/touchpadx/src/kcm/xlib/ui/kded.ui b/kcms/touchpadx/src/kcm/xlib/ui/kded.ui new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/ui/kded.ui @@ -0,0 +1,201 @@ + + + KdedForm + + + + 0 + 0 + 486 + 351 + + + + Enable/Disable Touchpad + + + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + Configure... + + + + + + + + + + + + Keyboard shortcuts: + + + configureShortcutsButton + + + + + + + + + + Disable touchpad when typing + + + true + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Timeout: + + + kcfg_KeyboardActivityTimeoutMs + + + + + + + If there was no keyboard activity for more than specified +number of milliseconds, touchpad will be enabled again + + + 0 + + + 99 + + + 10 + + + ms + + + + + + + Disable click emulation and scrolling. +You will be still able to move pointer using touchpad and perform clicks with hardware buttons. + + + Disable taps and scrolling only + + + + + + + + + + Disable touchpad when mouse is plugged in + + + true + + + true + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + Notifications: + + + configureNotificationsButton + + + + + + + Configure... + + + + + + + + + + + + Ignored devices: + + + kcfg_MouseBlacklist + + + + + + + Some devices might be incorrectly treated as a mouse. +You can add them to this list so that they will be ignored when they're plugged in. + + + KEditListWidget::Add|KEditListWidget::Remove + + + + + + + + + + + KEditListWidget + QGroupBox +
keditlistwidget.h
+
+ + QSpinBox + QWidget + +
+ + configureShortcutsButton + kcfg_DisableOnKeyboardActivity + kcfg_KeyboardActivityTimeoutMs + kcfg_OnlyDisableTapAndScrollOnKeyboardActivity + kcfg_DisableWhenMousePluggedIn + + + +
diff --git a/kcms/touchpadx/src/kcm/xlib/ui/pointermotion.ui b/kcms/touchpadx/src/kcm/xlib/ui/pointermotion.ui new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/ui/pointermotion.ui @@ -0,0 +1,288 @@ + + + PointerMotionForm + + + + 0 + 0 + 400 + 407 + + + + Pointer Motion + + + + + + Speed + + + true + + + + + + Minimum: + + + kcfg_MinSpeed + + + + + + + Minimum pointer speed + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + Maximum: + + + kcfg_MaxSpeed + + + + + + + Maximum pointer speed + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + Acceleration: + + + kcfg_AccelFactor + + + + + + + Acceleration factor for normal pointer movements + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + + Pressure-Dependent Motion + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Minimum pressure: + + + kcfg_PressureMotionMinZ + + + + + + + Minimum factor: + + + kcfg_PressureMotionMinFactor + + + + + + + Maximum pressure: + + + kcfg_PressureMotionMaxZ + + + + + + + Maximum factor: + + + kcfg_PressureMotionMaxFactor + + + + + + + Finger pressure at which minimum pressure motion factor is applied + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + Finger pressure at which maximum pressure motion factor is applied + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + Lowest setting for pressure motion factor + + + x + + + + + + + Greatest setting for pressure motion factor + + + x + + + + + + + + + + Noise Cancellation + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Vertical: + + + kcfg_VertHysteresis + + + + + + + Horizontal: + + + kcfg_HorizHysteresis + + + + + + + The minimum vertical hardware distance required to generate motion events + + + 0 + + + 10 + + + units + + + + + + + The minimum horizontal hardware distance required to generate motion events + + + 0 + + + 10 + + + units + + + + + + + + + + + CustomSlider + QSlider +
kcm/xlib/customslider.h
+
+
+ + kcfg_MinSpeed + kcfg_MaxSpeed + kcfg_AccelFactor + kcfg_PressureMotionMinZ + kcfg_PressureMotionMinFactor + kcfg_PressureMotionMaxZ + kcfg_PressureMotionMaxFactor + kcfg_VertHysteresis + kcfg_HorizHysteresis + + + +
diff --git a/kcms/touchpadx/src/kcm/xlib/ui/scroll.ui b/kcms/touchpadx/src/kcm/xlib/ui/scroll.ui new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/ui/scroll.ui @@ -0,0 +1,393 @@ + + + ScrollForm + + + + 0 + 0 + 610 + 503 + + + + Scrolling + + + + + + QFormLayout::FieldsStayAtSizeHint + + + Qt::AlignHCenter|Qt::AlignTop + + + + + Edge scrolling: + + + kcfg_VertEdgeScroll + + + + + + + Enable vertical scrolling when dragging along the right edge + + + Vertical + + + + + + + Enable horizontal scrolling when dragging along the bottom edge + + + Horizontal + + + + + + + Enable vertical scrolling when dragging with two fingers anywhere on the touchpad + + + Vertical + + + + + + + Enable horizontal scrolling when dragging with two fingers anywhere on the touchpad + + + Horizontal + + + + + + + Reverse vertical scrolling + + + Vertical + + + + + + + Reverse horizontal scrolling + + + Horizontal + + + + + + + Two-finger scrolling: + + + kcfg_VertTwoFingerScroll + + + + + + + Reverse scrolling: + + + kcfg_InvertVertScroll + + + + + + + + + + 300 + 0 + + + + Scrolling Distance + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Vertical: + + + kcfg_VertScrollDelta + + + + + + + Move distance of the finger for a scroll event + + + 1000.000000000000000 + + + 0.100000000000000 + + + mm + + + 1 + + + + + + + Horizontal: + + + kcfg_HorizScrollDelta + + + + + + + Move distance of the finger for a scroll event + + + 1000.000000000000000 + + + 0.100000000000000 + + + mm + + + 1 + + + + + + + + + + Continue scrolling after the finger is released from the edge of the touchpad + + + Coasting + + + true + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Minimum speed: + + + kcfg_CoastingSpeed + + + + + + + Your finger needs to produce this many scrolls per second in order to start coasting + + + 1.000000000000000 + + + scrolls/sec + + + + + + + Deceleration: + + + kcfg_CoastingFriction + + + + + + + Number of scrolls/second² to decrease the coasting speed + + + 1.000000000000000 + + + scrolls/sec² + + + + + + + Corner coasting: + + + kcfg_CornerCoasting + + + + + + + Enable edge scrolling to continue while the finger stays in an edge corner + + + Enable + + + + + + + + + + Scrolling is engaged when a drag starts in the given trigger region. +Moving your finger in clockwise circles around the center of the touchpad +will scroll down and counter clockwise motion will scroll up + + + Circular Scrolling + + + true + + + true + + + + QFormLayout::ExpandingFieldsGrow + + + + + Trigger region: + + + kcfg_CircScrollTrigger + + + + + + + Trigger region on the touchpad to start circular scrolling + + + + + + + Angle: + + + kcfg_CircScrollDelta + + + + + + + + 0 + 0 + + + + 45.000000000000000 + + + 0.100000000000000 + + + Move angle (degrees) of finger to generate a scroll event + + + ° + + + + + + + When used together with vertical scrolling, hitting the upper or lower right corner will seamlessly switch over from vertical to circular scrolling + + + true + + + + + + + + + + + KComboBox + QComboBox +
kcombobox.h
+
+
+ + kcfg_VertEdgeScroll + kcfg_HorizEdgeScroll + kcfg_VertTwoFingerScroll + kcfg_HorizTwoFingerScroll + kcfg_InvertVertScroll + kcfg_InvertHorizScroll + kcfg_VertScrollDelta + kcfg_HorizScrollDelta + kcfg_Coasting + kcfg_CoastingSpeed + kcfg_CoastingFriction + kcfg_CornerCoasting + kcfg_CircularScrolling + kcfg_CircScrollTrigger + kcfg_CircScrollDelta + + + +
diff --git a/kcms/touchpadx/src/kcm/xlib/ui/sensitivity.ui b/kcms/touchpadx/src/kcm/xlib/ui/sensitivity.ui new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/ui/sensitivity.ui @@ -0,0 +1,153 @@ + + + SensitivityForm + + + + 0 + 0 + 430 + 210 + + + + Sensitivity + + + + + + Sensitivity + + + true + + + + + + Pressure for detecting a touch: + + + kcfg_FingerHigh + + + + + + + Pressure for detecting a release: + + + kcfg_FingerLow + + + + + + + When finger pressure goes above this value, the driver counts it as a touch + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + When finger pressure drops below this value, the driver counts it as a release + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + + If palm detection should be enabled. Note that this also requires hardware/firmware support from the touchpad + + + Palm Detection + + + true + + + true + + + + + + Minimum width: + + + kcfg_PalmMinWidth + + + + + + + Minimum pressure: + + + kcfg_PalmMinZ + + + + + + + Minimum finger width at which touch is considered a palm + + + 1 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + Minimum finger pressure at which touch is considered a palm + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + + kcfg_FingerHigh + kcfg_FingerLow + kcfg_PalmDetect + kcfg_PalmMinWidth + kcfg_PalmMinZ + + + + diff --git a/kcms/touchpadx/src/kcm/xlib/ui/tap.ui b/kcms/touchpadx/src/kcm/xlib/ui/tap.ui new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/ui/tap.ui @@ -0,0 +1,500 @@ + + + TapForm + + + + 0 + 0 + 516 + 531 + + + + Taps + + + + + + + + + 0 + 0 + + + + Mouse Click Emulation + + + true + + + true + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + One finger: + + + kcfg_OneFingerTapButton + + + + + + + Which mouse button is reported on a non-corner one-finger tap + + + + + + + Two fingers: + + + kcfg_TwoFingerTapButton + + + + + + + Which mouse button is reported on a non-corner two-finger tap + + + + + + + Three fingers: + + + kcfg_ThreeFingerTapButton + + + + + + + Which mouse button is reported on a non-corner three-finger tap + + + + + + + + + + + + + + + 0 + 0 + + + + Corners + + + true + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Top left: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + kcfg_LTCornerButton + + + + + + + Which mouse button is reported on a left top corner tap + + + + + + + Bottom left: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + kcfg_LBCornerButton + + + + + + + Top right: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + kcfg_RTCornerButton + + + + + + + Which mouse button is reported on a left bottom corner tap + + + + + + + Which mouse button is reported on a right top corner tap + + + + + + + Which mouse button is reported on a right bottom corner tap + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 30 + 20 + + + + + + + + Bottom right: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + kcfg_RBCornerButton + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + + + + + This gesture is an alternative way of dragging. It is performed by tapping (touching and releasing the finger), then touching again and moving the finger on the touchpad + + + Tap-and-Drag Gesture + + + true + + + true + + + + + + If off, a tap-and-drag gesture ends when you release the finger. If on, the gesture is active until you tap a second time, or until timeout expires + + + Locked Drags + + + false + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Timeout: + + + kcfg_LockedDragTimeout + + + + + + + How long it takes (in milliseconds) for the "Locked Drags" mode to be automatically turned off after the finger is released from the touchpad + + + 0 + + + 99 + + + 99 + + + ms + + + + + + + + + + + + + + 300 + 0 + + + + Tap Detection + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + + + Maximum time: + + + kcfg_MaxTapTime + + + + + + + + + + Maximum finger movement: + + + kcfg_MaxTapMove + + + + + + + + + + Maximum time for double tap: + + + kcfg_MaxDoubleTapTime + + + + + + + + + + Single tap timeout: + + + kcfg_SingleTapTimeout + + + + + + + Maximum time (in milliseconds) for detecting a tap + + + 0 + + + 99 + + + 10 + + + ms + + + + + + + Maximum movement of the finger for detecting a tap + + + 1000.000000000000000 + + + 0.100000000000000 + + + mm + + + 1 + + + + + + + Maximum time (in milliseconds) for detecting a double tap + + + 0 + + + 99 + + + 10 + + + ms + + + + + + + Timeout after a tap to recognize it as a single tap + + + 0 + + + 99 + + + 10 + + + ms + + + + + + + + + + + QSpinBox + QWidget + + + KComboBox + QComboBox +
kcombobox.h
+
+ + QSpinBox + QWidget + +
+ + kcfg_OneFingerTapButton + kcfg_TwoFingerTapButton + kcfg_ThreeFingerTapButton + kcfg_LTCornerButton + kcfg_RTCornerButton + kcfg_LBCornerButton + kcfg_RBCornerButton + kcfg_TapAndDragGesture + kcfg_LockedDrags + kcfg_LockedDragTimeout + kcfg_MaxTapTime + kcfg_MaxTapMove + kcfg_MaxDoubleTapTime + kcfg_SingleTapTimeout + + + +
diff --git a/kcms/touchpadx/src/kcm/xlib/ui/testarea.ui b/kcms/touchpadx/src/kcm/xlib/ui/testarea.ui new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kcm/xlib/ui/testarea.ui @@ -0,0 +1,72 @@ + + + TestArea + + + + 0 + 0 + 200 + 629 + + + + true + + + + + + Testing area + + + + + + Qt::NoFocus + + + Click me + + + + + + + Qt::NoFocus + + + + + 2000 + 2000 + + + + + + + + + Qt::NoFocus + + + QListView::IconMode + + + + + + + + + + + TestButton + QPushButton +
kcm/xlib/testbutton.h
+
+
+ + +
diff --git a/kcms/touchpadx/src/kded/kcm_touchpad.notifyrc b/kcms/touchpadx/src/kded/kcm_touchpad.notifyrc new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/kcm_touchpad.notifyrc @@ -0,0 +1,258 @@ +[Global] +Name=Touchpad +Name[ar]=لوحة اللمس +Name[ca]=Ratolí tàctil +Name[ca@valencia]=Ratolí tàctil +Name[cs]=Touchpad +Name[da]=Touchpad +Name[de]=Touchpad +Name[el]=Επιφάνεια αφής +Name[en_GB]=Touchpad +Name[es]=Panel táctil +Name[et]=Puutepadi +Name[eu]=Ukimen-sagua +Name[fi]=Kosketuslevy +Name[fr]=Pavé tactile +Name[gl]=Área táctil +Name[he]=לוח מגע +Name[hu]=Érintőtábla +Name[id]=Touchpad +Name[is]=Snertiplatti +Name[it]=Touchpad +Name[ja]=タッチパッド +Name[ko]=터치패드 +Name[lt]=Jutiklinis kilimėlis +Name[nl]=Touchpad +Name[nn]=Styreplate +Name[pa]=ਟੱਚਪੈਡ +Name[pl]=Gładzik +Name[pt]=Rato por Toque +Name[pt_BR]=Touchpad +Name[ru]=Сенсорная панель +Name[sk]=Touchpad +Name[sl]=Sledilna ploščica +Name[sr]=Додирник +Name[sr@ijekavian]=Додирник +Name[sr@ijekavianlatin]=Dodirnik +Name[sr@latin]=Dodirnik +Name[sv]=Tryckplatta +Name[tr]=Dokunmatik yüzey +Name[uk]=Сенсорна панель +Name[x-test]=xxTouchpadxx +Name[zh_CN]=触摸板 +Name[zh_TW]=觸控板 +IconName=input-touchpad +Comment=Touchpad +Comment[ar]=لوحة اللمس +Comment[ca]=Ratolí tàctil +Comment[ca@valencia]=Ratolí tàctil +Comment[cs]=Touchpad +Comment[da]=Touchpad +Comment[de]=Touchpad +Comment[el]=Επιφάνεια αφής +Comment[en_GB]=Touchpad +Comment[es]=Panel táctil +Comment[et]=Puutepadi +Comment[eu]=Ukimen-sagua +Comment[fi]=Kosketuslevy +Comment[fr]=Pavé tactile +Comment[gl]=Área táctil +Comment[he]=לוח מגע +Comment[hu]=Érintőtábla +Comment[id]=Touchpad +Comment[is]=Snertiplatti +Comment[it]=Touchpad +Comment[ja]=タッチパッド +Comment[ko]=터치패드 +Comment[lt]=Jutiklinis kilimėlis +Comment[nl]=Touchpad +Comment[nn]=Styreplate +Comment[pa]=ਟੱਚਪੈਡ +Comment[pl]=Gładzik +Comment[pt]=Rato por Toque +Comment[pt_BR]=Touchpad +Comment[ru]=Сенсорная панель +Comment[sk]=Touchpad +Comment[sl]=Sledilna ploščica +Comment[sr]=Додирник +Comment[sr@ijekavian]=Додирник +Comment[sr@ijekavianlatin]=Dodirnik +Comment[sr@latin]=Dodirnik +Comment[sv]=Tryckplatta +Comment[tr]=Dokunmatik yüzey +Comment[uk]=Сенсорна панель +Comment[x-test]=xxTouchpadxx +Comment[zh_CN]=触摸板 +Comment[zh_TW]=觸控板 + +[Event/TouchpadDisabled] +Name=Touchpad was disabled +Name[ar]=عُطّلت لوحة اللمس +Name[ca]=El ratolí tàctil s'ha desactivat +Name[ca@valencia]=El ratolí tàctil s'ha desactivat +Name[cs]=Touchpad byl zakázán +Name[da]=Touchpad deaktiveret +Name[de]=Touchpad wurde deaktiviert +Name[el]=Η επιφάνεια αφής απενεργοποιήθηκε +Name[en_GB]=Touchpad was disabled +Name[es]=El panel táctil ha sido desactivado +Name[et]=Puutepadi on väljas +Name[eu]=Ukimen-sagua desgaitu da +Name[fi]=Kosketuslevy poistettiin käytöstä +Name[fr]=Le pavé tactile a été désactivé +Name[gl]=Desactivouse a área táctil +Name[he]=לוח מגע מבוטל +Name[hu]=Az érintőtábla nincs bekapcsolva +Name[id]=Touchpad telah dinonaktifkan +Name[it]=Il touchpad è stato disabilitato +Name[ja]=タッチパッドが無効になりました +Name[ko]=터치패드 비활성화됨 +Name[lt]=Jutiklinis kilimėlis buvo išjungtas +Name[nl]=Touchpad was uitgeschakeld +Name[nn]=Styreplata vart slått av +Name[pa]=ਟੱਚਪੈਡ ਬੰਦ ਸੀ +Name[pl]=Gładzik został wyłączony +Name[pt]=O rato por toque foi desactivado +Name[pt_BR]=O touchpad foi desativado +Name[ru]=Сенсорная панель отключена +Name[sk]=Touchpad bol zakázaný +Name[sl]=Sledilna ploščica je bila onemogočena +Name[sr]=Додирник деактивиран +Name[sr@ijekavian]=Додирник деактивиран +Name[sr@ijekavianlatin]=Dodirnik deaktiviran +Name[sr@latin]=Dodirnik deaktiviran +Name[sv]=Tryckplattan är inaktiverad +Name[tr]=Dokunmatik yüzey kapalı +Name[uk]=Сенсорну панель вимкнено +Name[x-test]=xxTouchpad was disabledxx +Name[zh_CN]=触摸板已禁用 +Name[zh_TW]=觸控板已關閉 +Comment=Touchpad was disabled because mouse is detected +Comment[ar]=عُطّلت لوحة اللمس بسبب اكتشاف فأرة +Comment[ca]=El ratolí tàctil s'ha desactivat perquè s'ha detectat un ratolí +Comment[ca@valencia]=El ratolí tàctil s'ha desactivat perquè s'ha detectat un ratolí +Comment[cs]=Touchpad byl zakázán protože byla zjištěna myš +Comment[da]=Touchpad blev deaktiveret fordi musen blev detekteret +Comment[de]=Das Touchpad wurde deaktiviert, weil eine Maus erkannt wurde +Comment[el]=Η επιφάνεια αφής απενεργοποιήθηκε γιατί ανιχνεύτηκε ένα ποντίκι +Comment[en_GB]=Touchpad was disabled because mouse is detected +Comment[es]=El panel táctil ha sido desactivado porque se ha detectado un ratón +Comment[et]=Puutepadi on väljas, sest tuvastati hiir +Comment[eu]=Ukimen-sagua desgaitu da sagua detektatu delako +Comment[fi]=Kosketuslevy poistettiin käytöstä, koska havaittiin hiiri +Comment[fr]=Le pavé tactile a été désactivé car une souris a été détectée +Comment[gl]=Desactivouse a área táctil porque se detectou un rato. +Comment[he]=לוח המגע מבוטל בגלל שזוהה עכבר +Comment[hu]=Egér észlelve, az érintőtábla kikapcsolva +Comment[id]=Touchpad telah dinonaktifkan karena mouse telah terdeteksi +Comment[it]=Il touchpad è stato disabilitato poiché è stato rilevato un mouse +Comment[ja]=マウスが検出されたため、タッチパッドが無効になりました +Comment[ko]=마우스가 감지되어 터치패드 비활성화됨 +Comment[lt]=Jutiklinis kilimėlis buvo išjungtas nes yra aptikta pelė +Comment[nl]=Touchpad was uitgeschakeld omdat er een muis is gedetecteerd +Comment[nn]=Styreplata vart slått av fordi det vart kopla til ei mus +Comment[pa]=ਟੱਚਪੈਡ ਬੰਦ ਕੀਤੀ ਸੀ ਕਿਉਂਕਿ ਮਾਊਸ ਮਿਲਿਆ ਸੀ +Comment[pl]=Gładzik został wyłączony, bo podłączono mysz +Comment[pt]=O rato por toque foi desactivado porque foi detectado um rato +Comment[pt_BR]=O touchpad foi desativado porque o mouse foi detectado +Comment[ru]=Сенсорная панель была отключена в связи с обнаружением мыши +Comment[sk]=Touchpad bol zakázaný, lebo bola zistená myš +Comment[sl]=Sledilna ploščica je bila onemogočena, ker je bila zaznana miška +Comment[sr]=Додирник је деактивиран јер је откривен миш. +Comment[sr@ijekavian]=Додирник је деактивиран јер је откривен миш. +Comment[sr@ijekavianlatin]=Dodirnik je deaktiviran jer je otkriven miš. +Comment[sr@latin]=Dodirnik je deaktiviran jer je otkriven miš. +Comment[sv]=Tryckplattan är inaktiverad eftersom en mus detekterades +Comment[tr]=Fare algılandığından dokunmatik yüzey kapalı +Comment[uk]=Сенсорну панель вимкнено, оскільки виявлено мишу +Comment[x-test]=xxTouchpad was disabled because mouse is detectedxx +Comment[zh_CN]=因为检测到鼠标,触摸板已禁用 +Comment[zh_TW]=觸控板已關閉,因為偵測到外接滑鼠 +Sound=Oxygen-Sys-App-Positive.ogg +Action=Popup + +[Event/TouchpadEnabled] +Name=Touchpad was enabled +Name[ar]=فُعّلت لوحة اللمس +Name[ca]=El ratolí tàctil s'ha activat +Name[ca@valencia]=El ratolí tàctil s'ha activat +Name[cs]=Touchpad byl povolen +Name[da]=Touchpad aktiveret +Name[de]=Touchpad wurde aktiviert +Name[el]=Η επιφάνεια αφής ενεργοποιήθηκε +Name[en_GB]=Touchpad was enabled +Name[es]=El panel táctil ha sido activado +Name[et]=Puutepadi on sees +Name[eu]=Ukimen-sagua gaitu da +Name[fi]=Kosketuslevy otettiin käyttöön +Name[fr]=Le pavé tactile a été activé +Name[gl]=Activouse a área táctil +Name[he]=לוח המגע מופעל +Name[hu]=Az érintőtábla be van kapcsolva +Name[id]=Touchpad telah diaktifkan +Name[it]=Il touchpad è stato abilitato +Name[ja]=タッチパッドが有効になりました +Name[ko]=터치패드 활성화됨 +Name[lt]=Jutiklinis kilimėlis buvo įjungtas +Name[nl]=Touchpad was ingeschakeld +Name[nn]=Styreplate vart slått på +Name[pa]=ਟੱਚਪੈਡ ਸਮਰੱਥ ਸੀ +Name[pl]=Gładzik został włączony +Name[pt]=O rato por toque foi activado +Name[pt_BR]=O touchpad foi ativado +Name[ru]=Сенсорная панель включена +Name[sk]=Touchpad bol povolený +Name[sl]=Sledilna ploščica je bila omogočena +Name[sr]=Додирник активиран +Name[sr@ijekavian]=Додирник активиран +Name[sr@ijekavianlatin]=Dodirnik aktiviran +Name[sr@latin]=Dodirnik aktiviran +Name[sv]=Tryckplattan är aktiverad +Name[tr]=Dokunmatik yüzey etkin +Name[uk]=Сенсорну панель увімкнено +Name[x-test]=xxTouchpad was enabledxx +Name[zh_CN]=触摸板已启用 +Name[zh_TW]=觸控板已開啟 +Comment=Touchpad was enabled because the mouse was unplugged +Comment[ar]=فُعّلت لوحة اللمس بسبب فصل الفأرة +Comment[ca]=El ratolí tàctil s'ha activat perquè en ratolí s'ha desendollat +Comment[ca@valencia]=El ratolí tàctil s'ha activat perquè en ratolí s'ha desendollat +Comment[cs]=Touchpad byl povolen protože byla odpojena myš +Comment[da]=Touchpad blev aktiveret fordi musen blev frakoblet +Comment[de]=Das Touchpad wurde aktiviert, weil eine Maus abgezogen wurde +Comment[el]=Η επιφάνεια αφής ενεργοποιήθηκε γιατί το ποντίκι αποσυνδέθηκε +Comment[en_GB]=Touchpad was enabled because the mouse was unplugged +Comment[es]=El panel táctil ha sido activado porque se ha desconectado el ratón +Comment[et]=Puutepadi on sees, sest hiir eemaldati +Comment[eu]=Ukimen-sagua gaitu da sagua deskonektatu delako +Comment[fi]=Kosketuslevy otettiin käyttöön, koska hiiri irrotettiin +Comment[fr]=Le pavé tactile a été activé car la souris a été débranchée +Comment[gl]=Activouse a área táctil porque se desconectou o rato. +Comment[he]=לוח המגע הופעל בגלל שהעכבר נותק +Comment[hu]=Az egeret kihúzták, az érintőtábla bekapcsolt +Comment[id]=Touchpad telah diaktifkan karena mouse telah tercabut +Comment[it]=Il touchpad è stato abilitato poiché il mouse è stato scollegato +Comment[ja]=マウスが外されたため、タッチパッドが有効になりました +Comment[ko]=마우스가 연결 해제되어 터치패드 활성화됨 +Comment[lt]=Jutiklinis kilimėlis įjungtas, nes buvo atjungta pelė +Comment[nl]=Touchpad was ingeschakeld omdat er een muis is uitgetrokken +Comment[nn]=Styreplata vart slått på fordi ei mus vart kopla frå +Comment[pa]=ਟੱਚਪੈਡ ਸਮਰੱਥ ਸੀ, ਕਿਉਂਕਿ ਮਾਊਂਸ ਨੂੰ ਜੋੜਿਆ ਨਹੀਂ ਗਿਆ ਸੀ +Comment[pl]=Gładzik został włączony, bo odłączono mysz +Comment[pt]=O rato por toque foi activado porque o rato foi desligado +Comment[pt_BR]=O touchpad foi ativado porque o mouse foi desconectado +Comment[ru]=Сенсорная панель была включена в связи с отключением мыши +Comment[sk]=Touchpad bol povolený, pretože myš bola odpojená +Comment[sl]=Sledilna ploščica je bila omogočena, ker je bila miška odklopljena +Comment[sr]=Додирник је активиран јер је миш извучен. +Comment[sr@ijekavian]=Додирник је активиран јер је миш извучен. +Comment[sr@ijekavianlatin]=Dodirnik je aktiviran jer je miš izvučen. +Comment[sr@latin]=Dodirnik je aktiviran jer je miš izvučen. +Comment[sv]=Tryckplattan är aktiverad eftersom musen kopplades bort +Comment[tr]=Fare çıkartıldığından dokunmatik yüzey etkinleştirildi +Comment[uk]=Сенсорну панель увімкнено, оскільки мишу було від’єднано +Comment[x-test]=xxTouchpad was enabled because the mouse was unpluggedxx +Comment[zh_CN]=因为鼠标已拔出,触摸板已禁用 +Comment[zh_TW]=觸控板已開啟,因為偵測到外接滑鼠已被拔除 +Sound=Oxygen-Sys-App-Negative.ogg +Action=Sound diff --git a/kcms/touchpadx/src/kded/kded.h b/kcms/touchpadx/src/kded/kded.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/kded.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 KDED_H +#define KDED_H + +#include +#include +#include +#include + +#include + +#include "touchpadbackend.h" +#include "kdedsettings.h" + +class TouchpadDisabler : public KDEDModule +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.touchpad") + +public: + TouchpadDisabler(QObject *, const QVariantList &); + +Q_SIGNALS: + Q_SCRIPTABLE void enabledChanged(bool); + Q_SCRIPTABLE void mousePluggedInChanged(bool); + Q_SCRIPTABLE void workingTouchpadFoundChanged(bool); + +public Q_SLOTS: + Q_SCRIPTABLE Q_NOREPLY void reloadSettings(); + Q_SCRIPTABLE Q_NOREPLY void toggle(); + Q_SCRIPTABLE Q_NOREPLY void disable(); + Q_SCRIPTABLE Q_NOREPLY void enable(); + Q_SCRIPTABLE bool isEnabled() const; + Q_SCRIPTABLE bool workingTouchpadFound() const; + Q_SCRIPTABLE bool isMousePluggedIn() const; + +private Q_SLOTS: + void keyboardActivityStarted(); + void keyboardActivityFinished(); + void timerElapsed(); + void mousePlugged(); + void updateCurrentState(); + void serviceRegistered(const QString &); + void handleReset(); + void serviceNameFetchFinished(QDBusPendingCallWatcher *); + void onPrepareForSleep(bool sleep); + +private: + void showNotification(const QString &name, const QString &text); + void lateInit(); + void updateWorkingTouchpadFound(); + void showOsd(); + + TouchpadBackend *m_backend; + TouchpadDisablerSettings m_settings; + QTimer m_keyboardActivityTimeout; + QDBusServiceWatcher m_dependecies; + + TouchpadBackend::TouchpadOffState m_keyboardDisableState; + bool m_userRequestedState, m_touchpadEnabled; + bool m_workingTouchpadFound; + bool m_keyboardActivity, m_mouse; + + bool m_preparingForSleep = false; +}; + +#endif // KDED_H diff --git a/kcms/touchpadx/src/kded/kded.cpp b/kcms/touchpadx/src/kded/kded.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/kded.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "kded.h" + +#include +#include +#include +#include +#include +#include + +#include "plugins.h" +#include "kdedactions.h" + +bool TouchpadDisabler::workingTouchpadFound() const +{ + return m_workingTouchpadFound; +} + +void TouchpadDisabler::serviceRegistered(const QString &service) +{ + if (!m_dependecies.removeWatchedService(service)) { + return; + } + + if (m_dependecies.watchedServices().isEmpty()) { + lateInit(); + } +} + +TouchpadDisabler::TouchpadDisabler(QObject *parent, const QVariantList &) + : KDEDModule(parent), m_backend(TouchpadBackend::implementation()), + m_userRequestedState(true), m_touchpadEnabled(true), m_workingTouchpadFound(false), m_keyboardActivity(false), m_mouse(false) +{ + if (!m_backend) { + return; + } + + m_dependecies.addWatchedService("org.kde.plasmashell"); + m_dependecies.addWatchedService("org.kde.kglobalaccel"); + connect(&m_dependecies, SIGNAL(serviceRegistered(QString)), + SLOT(serviceRegistered(QString))); + + connect(m_backend, SIGNAL(mousesChanged()), SLOT(mousePlugged())); + connect(m_backend, SIGNAL(keyboardActivityStarted()), + SLOT(keyboardActivityStarted())); + connect(m_backend, SIGNAL(keyboardActivityFinished()), + SLOT(keyboardActivityFinished())); + connect(m_backend, SIGNAL(touchpadStateChanged()), + SLOT(updateCurrentState())); + + connect(m_backend, SIGNAL(touchpadReset()), SLOT(handleReset())); + + m_keyboardActivityTimeout.setSingleShot(true); + connect(&m_keyboardActivityTimeout, SIGNAL(timeout()), + SLOT(timerElapsed())); + + updateCurrentState(); + m_userRequestedState = m_touchpadEnabled; + reloadSettings(); + + m_dependecies.setWatchMode(QDBusServiceWatcher::WatchForRegistration); + m_dependecies.setConnection(QDBusConnection::sessionBus()); + QDBusPendingCall async = QDBusConnection::sessionBus().interface()->asyncCall(QLatin1String("ListNames")); + QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this); + connect(callWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(serviceNameFetchFinished(QDBusPendingCallWatcher*))); + + + QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.login1"), + QStringLiteral("/org/freedesktop/login1"), + QStringLiteral("org.freedesktop.login1.Manager"), + QStringLiteral("PrepareForSleep"), + this, + SLOT(onPrepareForSleep(bool))); +} + +void TouchpadDisabler::serviceNameFetchFinished(QDBusPendingCallWatcher *callWatcher) +{ + QDBusPendingReply reply = *callWatcher; + callWatcher->deleteLater(); + + if (reply.isError() || reply.value().isEmpty()) { + qWarning() << "Error: Couldn't get registered services list from session bus"; + return; + } + + QStringList allServices = reply.value(); + Q_FOREACH (const QString &service, m_dependecies.watchedServices()) { + if (allServices.contains(service)) { + serviceRegistered(service); + } + } +} + +bool TouchpadDisabler::isEnabled() const +{ + return m_touchpadEnabled; +} + +void TouchpadDisabler::updateCurrentState() +{ + updateWorkingTouchpadFound(); + if (!m_backend->isTouchpadAvailable()) { + return; + } + bool newEnabled = m_backend->isTouchpadEnabled(); + if (newEnabled != m_touchpadEnabled) { + m_touchpadEnabled = newEnabled; + Q_EMIT enabledChanged(m_touchpadEnabled); + } +} + +void TouchpadDisabler::toggle() +{ + m_userRequestedState = !m_touchpadEnabled; + m_backend->setTouchpadEnabled(m_userRequestedState); +} + +void TouchpadDisabler::disable() +{ + m_userRequestedState = false; + m_backend->setTouchpadEnabled(false); +} + +void TouchpadDisabler::enable() +{ + m_userRequestedState = true; + m_backend->setTouchpadEnabled(true); +} + +void TouchpadDisabler::reloadSettings() +{ + m_settings.load(); + m_keyboardActivityTimeout.setInterval( + m_settings.keyboardActivityTimeoutMs()); + + m_keyboardDisableState = + m_settings.onlyDisableTapAndScrollOnKeyboardActivity() ? + TouchpadBackend::TouchpadTapAndScrollDisabled : + TouchpadBackend::TouchpadFullyDisabled; + + mousePlugged(); + + m_backend->watchForEvents(m_settings.disableOnKeyboardActivity()); +} + +void TouchpadDisabler::keyboardActivityStarted() +{ + if (m_keyboardActivity || !m_settings.disableOnKeyboardActivity()) { + return; + } + + m_keyboardActivityTimeout.stop(); + m_keyboardActivity = true; + m_backend->setTouchpadOff(m_keyboardDisableState); +} + +void TouchpadDisabler::keyboardActivityFinished() +{ + if (!m_keyboardActivity) { + keyboardActivityStarted(); + } + m_keyboardActivityTimeout.start(); +} + +void TouchpadDisabler::timerElapsed() +{ + if (!m_keyboardActivity) { + return; + } + + m_keyboardActivity = false; + m_backend->setTouchpadOff(TouchpadBackend::TouchpadEnabled); +} + +void TouchpadDisabler::mousePlugged() +{ + if (!m_dependecies.watchedServices().isEmpty()) { + return; + } + + bool pluggedIn = isMousePluggedIn(); + Q_EMIT mousePluggedInChanged(pluggedIn); + + bool disable = pluggedIn && m_settings.disableWhenMousePluggedIn(); + if (m_mouse == disable) { + return; + } + m_mouse = disable; + + bool newState = disable ? false : m_userRequestedState; + if (newState == m_touchpadEnabled) { + return; + } + + // If the disable is caused by plugin mouse, show the message, otherwise it might + // be user already disables touchpad themselves. + if (!newState && disable) { + showNotification("TouchpadDisabled", + i18n("Touchpad was disabled because a mouse was plugged in")); + } + if (newState) { + showNotification("TouchpadEnabled", + i18n("Touchpad was enabled because the mouse was unplugged")); + } + + m_backend->setTouchpadEnabled(newState); +} + +void TouchpadDisabler::showNotification(const QString &name, const QString &text) +{ + KNotification::event(name, text, QPixmap(), //Icon is specified in .notifyrc + 0, + KNotification::CloseOnTimeout, + "kcm_touchpad"); // this has to match the name of the .notifyrc file + //TouchpadPluginFactory::componentData()); +} + +bool TouchpadDisabler::isMousePluggedIn() const +{ + return !m_backend->listMouses(m_settings.mouseBlacklist()).isEmpty(); +} + +void TouchpadDisabler::lateInit() +{ + TouchpadGlobalActions *actions = new TouchpadGlobalActions(false, this); + connect(actions, &TouchpadGlobalActions::enableTriggered, this, [this] { + enable(); + showOsd(); + }); + connect(actions, &TouchpadGlobalActions::disableTriggered, this, [this] { + disable(); + showOsd(); + }); + connect(actions, &TouchpadGlobalActions::toggleTriggered, this, [this] { + toggle(); + showOsd(); + }); + + updateCurrentState(); + mousePlugged(); +} + +void touchpadApplySavedConfig(); + +void TouchpadDisabler::handleReset() +{ + updateCurrentState(); + if (!m_workingTouchpadFound) { + return; + } + touchpadApplySavedConfig(); + m_backend->setTouchpadEnabled(m_userRequestedState); +} + +void TouchpadDisabler::updateWorkingTouchpadFound() +{ + bool newWorkingTouchpadFound = m_backend && m_backend->isTouchpadAvailable(); + if (newWorkingTouchpadFound != m_workingTouchpadFound) { + m_workingTouchpadFound = newWorkingTouchpadFound; + Q_EMIT workingTouchpadFoundChanged(m_workingTouchpadFound); + } +} + +void TouchpadDisabler::onPrepareForSleep(bool sleep) +{ + m_preparingForSleep = sleep; +} + +void TouchpadDisabler::showOsd() +{ + if (m_preparingForSleep) { + return; + } + + QDBusMessage msg = QDBusMessage::createMethodCall( + QStringLiteral("org.kde.plasmashell"), + QStringLiteral("/org/kde/osdService"), + QStringLiteral("org.kde.osdService"), + QStringLiteral("touchpadEnabledChanged") + ); + + msg.setArguments({m_backend->isTouchpadEnabled()}); + + QDBusConnection::sessionBus().asyncCall(msg); +} diff --git a/kcms/touchpadx/src/kded/kded_touchpad.desktop b/kcms/touchpadx/src/kded/kded_touchpad.desktop new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/kded_touchpad.desktop @@ -0,0 +1,91 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=kded_touchpad +X-KDE-PluginInfo-Name=touchpad +X-KDE-Kded-autoload=true +X-KDE-Kded-load-on-demand=true +X-KDE-OnlyShowOnQtPlatforms=xcb +Name=Touchpad +Name[ar]=لوحة اللمس +Name[ca]=Ratolí tàctil +Name[ca@valencia]=Ratolí tàctil +Name[cs]=Touchpad +Name[da]=Touchpad +Name[de]=Touchpad +Name[el]=Επιφάνεια αφής +Name[en_GB]=Touchpad +Name[es]=Panel táctil +Name[et]=Puutepadi +Name[eu]=Ukimen-sagua +Name[fi]=Kosketuslevy +Name[fr]=Pavé tactile +Name[gl]=Área táctil +Name[he]=לוח מגע +Name[hu]=Érintőtábla +Name[id]=Touchpad +Name[is]=Snertiplatti +Name[it]=Touchpad +Name[ja]=タッチパッド +Name[ko]=터치패드 +Name[lt]=Jutiklinis kilimėlis +Name[nl]=Touchpad +Name[nn]=Styreplate +Name[pa]=ਟੱਚਪੈਡ +Name[pl]=Gładzik +Name[pt]=Rato por Toque +Name[pt_BR]=Touchpad +Name[ru]=Сенсорная панель +Name[sk]=Touchpad +Name[sl]=Sledilna ploščica +Name[sr]=Додирник +Name[sr@ijekavian]=Додирник +Name[sr@ijekavianlatin]=Dodirnik +Name[sr@latin]=Dodirnik +Name[sv]=Tryckplatta +Name[tr]=Dokunmatik yüzey +Name[uk]=Сенсорна панель +Name[x-test]=xxTouchpadxx +Name[zh_CN]=触摸板 +Name[zh_TW]=觸控板 +Comment=Enables or disables touchpad +Comment[ar]=يفعّل لوحة اللمس أو يعطّلها +Comment[ca]=Activa o desactiva el ratolí tàctil +Comment[ca@valencia]=Activa o desactiva el ratolí tàctil +Comment[cs]=Povolí/zakáže touchpad +Comment[da]=Aktiverer eller deaktiverer touchpad +Comment[de]=Aktiviert oder deaktiviert das Touchpad +Comment[el]=Ενεργοποιεί ή απενεργοποιεί την επιφάνεια αφής +Comment[en_GB]=Enables or disables touchpad +Comment[es]=Activa o desactiva el panel táctil +Comment[et]=Puutepadja lubamine või keelamine +Comment[eu]=ukimen-sagua (des)gaitzen du +Comment[fi]=Ottaa kosketuslevyn käyttöön tai poistaa sen käytöstä +Comment[fr]=Active ou désactive le pavé tactile +Comment[gl]=Activa ou desactiva a área táctil +Comment[he]=אפשר או בטל את לוח המגע +Comment[hu]=Érintőtábla be/kikapcsolása +Comment[id]=Aktifkan atau nonaktifkan touchpad +Comment[it]=Abilita o disabilita il touchpad +Comment[ja]=タッチパッドを有効化または無効化する +Comment[ko]=터치패드 활성/비활성 +Comment[lt]=Įjungia arba išjungia jutiklinį kilimėlį +Comment[nl]=Schakelt het touchpad in of uit +Comment[nn]=Slå på/av styreplate +Comment[pa]=ਟੱਚਪੈਡ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕਰੋ +Comment[pl]=Włącza lub wyłącza gładzik +Comment[pt]=Activa ou desactiva o rato por toque +Comment[pt_BR]=Ativa ou desativa o touchpad +Comment[ru]=Включает или выключает сенсорную панель +Comment[sk]=Povolí alebo zakáže touchpad +Comment[sl]=Omogoči ali onemogoči sledilno ploščico +Comment[sr]=Активира или деактивира додирник +Comment[sr@ijekavian]=Активира или деактивира додирник +Comment[sr@ijekavianlatin]=Aktivira ili deaktivira dodirnik +Comment[sr@latin]=Aktivira ili deaktivira dodirnik +Comment[sv]=Aktiverar eller inaktiverar tryckplattan +Comment[tr]=Dokunmatik yüzeyi etkinleştirir veya kapatır +Comment[uk]=Вмикає або вимикає сенсорну панель +Comment[x-test]=xxEnables or disables touchpadxx +Comment[zh_CN]=启用或禁用触摸板 +Comment[zh_TW]=開啟或關閉觸控板 diff --git a/kcms/touchpadx/src/kded/kdedactions.h b/kcms/touchpadx/src/kded/kdedactions.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/kdedactions.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 KDEDACTIONS_H +#define KDEDACTIONS_H + +#include + +class TouchpadGlobalActions : public KActionCollection +{ + Q_OBJECT +public: + explicit TouchpadGlobalActions(bool isConfiguration, QObject *parent); + +Q_SIGNALS: + void enableTriggered(); + void disableTriggered(); + void toggleTriggered(); +}; + +#endif // KDEDACTIONS_H diff --git a/kcms/touchpadx/src/kded/kdedactions.cpp b/kcms/touchpadx/src/kded/kdedactions.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/kdedactions.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "kdedactions.h" + +#include +#include +#include +#include + +#include "plugins.h" + +TouchpadGlobalActions::TouchpadGlobalActions(bool isConfiguration, QObject *parent) + : KActionCollection(parent) +{ + //setComponentName(TouchpadPluginFactory::componentData()); + setComponentName("kcm_touchpad"); + + QAction *enable = addAction("Enable Touchpad"); + enable->setText(i18n("Enable Touchpad")); + connect(enable, SIGNAL(triggered()), SIGNAL(enableTriggered())); + bool okEnable = KGlobalAccel::setGlobalShortcut(enable, QKeySequence(Qt::Key_TouchpadOn)); + if (!okEnable) { + qWarning() << "Couldn't set global shortcut to Qt::Key_TouchpadOn. There's another program using it, otherwise file a bug against kcm_touchpad"; + } + + QAction *disable = addAction("Disable Touchpad"); + disable->setText(i18n("Disable Touchpad")); + connect(disable, SIGNAL(triggered()), SIGNAL(disableTriggered())); + bool okDisable = KGlobalAccel::setGlobalShortcut(disable, QKeySequence(Qt::Key_TouchpadOff)); + if (!okDisable) { + qWarning() << "Couldn't set global shortcut to Qt::Key_TouchpadOff. There's another program using it, otherwise file a bug against kcm_touchpad"; + } + + QAction *toggle = addAction("Toggle Touchpad"); + toggle->setText(i18n("Toggle Touchpad")); + connect(toggle, SIGNAL(triggered()), SIGNAL(toggleTriggered())); + bool okToggle = KGlobalAccel::setGlobalShortcut(toggle, QKeySequence(Qt::Key_TouchpadToggle)); + if (!okToggle) { + qWarning() << "Couldn't set global shortcut to Qt::Key_TouchpadToggle. There's another program using it, otherwise file a bug against kcm_touchpad"; + } + + Q_FOREACH (QAction *act, actions()) { + KActionCollection::setShortcutsConfigurable(act,true); + if (isConfiguration) { + act->setProperty("isConfigurationAction", true); + } + } +} diff --git a/kcms/touchpadx/src/kded/kdedsettings.kcfgc b/kcms/touchpadx/src/kded/kdedsettings.kcfgc new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/kdedsettings.kcfgc @@ -0,0 +1,5 @@ +File=touchpaddaemon.kcfg +ClassName=TouchpadDisablerSettings +Mutators=true +MemberVariables=private +Inherits=KCoreConfigSkeleton diff --git a/kcms/touchpadx/src/kded/touchpaddaemon.kcfg b/kcms/touchpadx/src/kded/touchpaddaemon.kcfg new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/kded/touchpaddaemon.kcfg @@ -0,0 +1,26 @@ + + + + + + false + + + true + + + true + + + 0 + 10000 + 250 + + + TPPS/2 IBM TrackPoint,USB Trackpoint pointing device,DualPoint Stick,ThinkPad USB Keyboard with TrackPoint + + + diff --git a/kcms/touchpadx/src/logging.h b/kcms/touchpadx/src/logging.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/logging.h @@ -0,0 +1,25 @@ +/* + * 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. + */ + +#ifndef KCM_TOUCHPAD_LOGGING_H +#define KCM_TOUCHPAD_LOGGING_H + +#include + +Q_DECLARE_LOGGING_CATEGORY(KCM_TOUCHPAD) +#endif diff --git a/kcms/touchpadx/src/logging.cpp b/kcms/touchpadx/src/logging.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/logging.cpp @@ -0,0 +1,20 @@ +/* + * 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. + */ + +#include "logging.h" +Q_LOGGING_CATEGORY(KCM_TOUCHPAD, "kcm_touchpad") diff --git a/kcms/touchpadx/src/plugins.h b/kcms/touchpadx/src/plugins.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/plugins.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 PLUGINS_H +#define PLUGINS_H + +#include + +K_PLUGIN_FACTORY_DECLARATION(TouchpadPluginFactory) + +#endif // PLUGINS_H diff --git a/kcms/touchpadx/src/plugins.cpp b/kcms/touchpadx/src/plugins.cpp new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/plugins.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 Alexander Mezin + * + * 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 "plugins.h" + +#include +#include + +#include "kcm/touchpadconfigcontainer.h" +#include "kded/kded.h" + + +K_PLUGIN_FACTORY(TouchpadPluginFactory, + registerPlugin(); + registerPlugin("kcm");) +#include +//K_EXPORT_PLUGIN(TouchpadPluginFactory(buildAboutData())) diff --git a/kcms/touchpadx/src/touchpadbackend.h b/kcms/touchpadx/src/touchpadbackend.h new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/touchpadbackend.h @@ -0,0 +1,76 @@ +/* + * Copyright 2017 Roman Gilg + * Copyright 2013 Alexander Mezin + * + * 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 TOUCHPADBACKEND_H +#define TOUCHPADBACKEND_H + +#include +#include +#include + +class Q_DECL_EXPORT TouchpadBackend : public QObject +{ + Q_OBJECT + +protected: + explicit TouchpadBackend(QObject *parent) : QObject(parent) {} + +public: + static TouchpadBackend *implementation(); + + 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 QStringList supportedParameters() const {return QStringList();} + virtual QString errorString() const {return QString();} + + virtual QVector getDevices() const { return QVector(); } + virtual int touchpadCount() const {return 0;} + + enum TouchpadOffState { + TouchpadEnabled, TouchpadTapAndScrollDisabled, TouchpadFullyDisabled + }; + virtual void setTouchpadOff(TouchpadOffState) {} + virtual TouchpadOffState getTouchpadOff() {return TouchpadFullyDisabled;} + + virtual bool isTouchpadAvailable() {return false;} + virtual bool isTouchpadEnabled() {return false;} + virtual void setTouchpadEnabled(bool) {} + + virtual void watchForEvents(bool keyboard) {} + + virtual QStringList listMouses(const QStringList &blacklist) {return QStringList();} + +Q_SIGNALS: + void touchpadStateChanged(); + void mousesChanged(); + void touchpadReset(); + void keyboardActivityStarted(); + void keyboardActivityFinished(); + + void touchpadAdded(bool success); + void touchpadRemoved(int index); +}; + +#endif // TOUCHPADBACKEND_H diff --git a/kcms/touchpad/src/touchpadbackend.cpp b/kcms/touchpadx/src/touchpadbackend.cpp copy from kcms/touchpad/src/touchpadbackend.cpp copy to kcms/touchpadx/src/touchpadbackend.cpp --- a/kcms/touchpad/src/touchpadbackend.cpp +++ b/kcms/touchpadx/src/touchpadbackend.cpp @@ -20,7 +20,6 @@ #include "touchpadbackend.h" #include "backends/x11/xlibbackend.h" -#include "backends/kwin_wayland/kwinwaylandbackend.h" #include "logging.h" #include @@ -39,11 +38,6 @@ } return backend.localData().data(); } - // TODO: test on kwin_wayland specifically? What about possibly other compositors under Wayland? - else if (KWindowSystem::isPlatformWayland()) { - qCDebug(KCM_TOUCHPAD) << "Using KWin+Wayland backend"; - return (new KWinWaylandBackend()); - } else { qCCritical(KCM_TOUCHPAD) << "Not able to select appropriate backend."; return nullptr; diff --git a/kcms/touchpadx/src/version.h.cmake b/kcms/touchpadx/src/version.h.cmake new file mode 100644 --- /dev/null +++ b/kcms/touchpadx/src/version.h.cmake @@ -0,0 +1,6 @@ +#ifndef VERSION_H +#define VERSION_H + +#define TOUCHPAD_KCM_VERSION "@TOUCHPAD_KCM_VERSION@" + +#endif // VERSION_H