diff --git a/kcms/input/CMakeLists.txt b/kcms/input/CMakeLists.txt index 7ec39d0c0..bf783c018 100644 --- a/kcms/input/CMakeLists.txt +++ b/kcms/input/CMakeLists.txt @@ -1,89 +1,89 @@ if(NOT X11_Xinput_FOUND) message(FATAL_ERROR "Xinput not found") endif() # KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"kcminput\") add_subdirectory( misc ) ## Add common files here. set(common_SRCS inputbackend.cpp ) include(ECMQtDeclareLoggingCategory) ecm_qt_declare_logging_category(common_SRCS HEADER logging.h IDENTIFIER KCM_INPUT CATEGORY_NAME kcm_input DEFAULT_SEVERITY Critical ) +set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) +qt5_add_dbus_interface(common_SRCS ${klauncher_xml} klauncher_iface) + include(backends/x11.cmake) include(backends/kwin_wl.cmake) ########### next target ############### add_executable(kapplymousetheme kapplymousetheme.cpp ${common_SRCS} ${backend_SRCS} ) target_link_libraries(kapplymousetheme ${backend_LIBS} Qt5::Gui Qt5::DBus KF5::CoreAddons KF5::ConfigCore KF5::I18n ) install(TARGETS kapplymousetheme ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) ########### next target ############### set(common_SRCS ${common_SRCS} plugin.cpp kcm/configcontainer.cpp kcm/configplugin.cpp kcm/libinput/libinput_config.cpp kcm/xlib/xlib_config.cpp ) -set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) - ki18n_wrap_ui(common_SRCS kcm/xlib/kcmmouse.ui) -qt5_add_dbus_interface(common_SRCS ${klauncher_xml} klauncher_iface) qt5_add_resources( common_SRCS kcm/resources.qrc ) add_library(kcm_input MODULE ${common_SRCS} ${backend_SRCS} ) target_link_libraries(kcm_input ${backend_LIBS} KF5::KCMUtils KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::KDELibs4Support KF5::Declarative Qt5::DBus Qt5::QuickWidgets ) install(TARGETS kcm_input DESTINATION ${KDE_INSTALL_PLUGINDIR} ) ########### install files ############### install( FILES mouse.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/kcms/input/backends/x11.cmake b/kcms/input/backends/x11.cmake index c0afb6565..9fdb53205 100644 --- a/kcms/input/backends/x11.cmake +++ b/kcms/input/backends/x11.cmake @@ -1,30 +1,34 @@ # // krazy:excludeall=copyright,license include_directories( ${X11_X11_INCLUDE_PATH} ${X11_Xinput_INCLUDE_PATH} ${Evdev_INCLUDE_DIRS} ${XORGLIBINPUT_INCLUDE_DIRS} ) set(backend_SRCS ${backend_SRCS} - backends/x11/evdev_settings.cpp backends/x11/x11_backend.cpp + backends/x11/x11_evdev_backend.cpp + backends/x11/evdev_settings.cpp + backends/x11/x11_libinput_backend.cpp + backends/x11/x11_libinput_dummydevice.cpp + backends/x11/libinput_settings.cpp ) set(backend_LIBS ${backend_LIBS} KF5::WindowSystem Qt5::X11Extras ${X11_X11_LIB} ${X11_Xinput_LIB} ) if (X11_Xcursor_FOUND) set(backend_LIBS ${X11_Xcursor_LIB} ${backend_LIBS} ) include_directories(${X11_Xcursor_INCLUDE_PATH}) endif () diff --git a/kcms/input/backends/x11/evdev_settings.cpp b/kcms/input/backends/x11/evdev_settings.cpp index 053e0b0d5..39bffeb1a 100644 --- a/kcms/input/backends/x11/evdev_settings.cpp +++ b/kcms/input/backends/x11/evdev_settings.cpp @@ -1,145 +1,139 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "evdev_settings.h" -#include "x11_backend.h" +#include "x11_evdev_backend.h" #include #include #include #include #include "../migrationlib/kdelibs4config.h" -void EvdevSettings::apply(X11Backend *backend, bool force) +void EvdevSettings::apply(X11EvdevBackend *backend, bool force) { if (!backend) { return; } backend->apply(force); handedNeedsApply = false; } -void EvdevSettings::load(X11Backend *backend) +void EvdevSettings::load(X11EvdevBackend *backend) { KConfig config("kcminputrc"); // TODO: what's a good threshold default value int threshold = 0; handed = Handed::Right; double accel = 1.0; - QString profile; if (backend) { auto handedOnServer = backend->handed(); handedEnabled = handedOnServer != Handed::NotSupported; if (handedEnabled) { handed = handedOnServer; } accel = backend->accelRate(); threshold = backend->threshold(); - profile = backend->accelerationProfile(); } KConfigGroup group = config.group("Mouse"); double a = group.readEntry("Acceleration", -1.0); if (a == -1) accelRate = accel; else accelRate = a; int t = group.readEntry("Threshold", -1); if (t == -1) thresholdMove = threshold; else thresholdMove = t; QString key = group.readEntry("MouseButtonMapping"); if (key == "RightHanded") handed = Handed::Right; else if (key == "LeftHanded") handed = Handed::Left; reverseScrollPolarity = group.readEntry("ReverseScrollPolarity", false); - currentAccelProfile = group.readEntry("AccelerationProfile"); - if (currentAccelProfile.isEmpty()) { - currentAccelProfile = profile; - } + handedNeedsApply = false; // SC/DC/AutoSelect/ChangeCursor group = config.group("KDE"); doubleClickInterval = group.readEntry("DoubleClickInterval", 400); dragStartTime = group.readEntry("StartDragTime", 500); dragStartDist = group.readEntry("StartDragDist", 4); wheelScrollLines = group.readEntry("WheelScrollLines", 3); singleClick = group.readEntry("SingleClick", true); } // see KGlobalSettings::emitChange enum ChangeType { PaletteChanged = 0, FontChanged, StyleChanged, SettingsChanged, IconChanged, CursorChanged, ToolbarStyleChanged, ClipboardConfigChanged, BlockShortcuts, NaturalSortingChanged }; enum SettingsCategory { SETTINGS_MOUSE, SETTINGS_COMPLETION, SETTINGS_PATHS, SETTINGS_POPUPMENU, SETTINGS_QT, SETTINGS_SHORTCUTS, SETTINGS_LOCALE, SETTINGS_STYLE }; static void emitChange(ChangeType changeType, int arg) { // see KGlobalSettings::emitChange QDBusMessage message = QDBusMessage::createSignal("/KGlobalSettings", "org.kde.KGlobalSettings", "notifyChange"); QList args; args.append(static_cast(changeType)); args.append(arg); message.setArguments(args); QDBusConnection::sessionBus().send(message); } void EvdevSettings::save() { KSharedConfig::Ptr kcminputProfile = KSharedConfig::openConfig("kcminputrc"); KConfigGroup kcminputGroup(kcminputProfile, "Mouse"); kcminputGroup.writeEntry("Acceleration",accelRate); kcminputGroup.writeEntry("Threshold",thresholdMove); if (handed == Handed::Right) { kcminputGroup.writeEntry("MouseButtonMapping",QString("RightHanded")); } else { kcminputGroup.writeEntry("MouseButtonMapping",QString("LeftHanded")); } kcminputGroup.writeEntry("ReverseScrollPolarity", reverseScrollPolarity); - kcminputGroup.writeEntry("AccelerationProfile", currentAccelProfile); kcminputGroup.sync(); KSharedConfig::Ptr profile = KSharedConfig::openConfig("kdeglobals"); KConfigGroup group(profile, "KDE"); group.writeEntry("DoubleClickInterval", doubleClickInterval, KConfig::Persistent); group.writeEntry("StartDragTime", dragStartTime, KConfig::Persistent); group.writeEntry("StartDragDist", dragStartDist, KConfig::Persistent); group.writeEntry("WheelScrollLines", wheelScrollLines, KConfig::Persistent); group.writeEntry("SingleClick", singleClick, KConfig::Persistent); group.sync(); kcminputProfile->sync(); Kdelibs4SharedConfig::syncConfigGroup(QLatin1String("Mouse"), "kcminputrc"); Kdelibs4SharedConfig::syncConfigGroup(QLatin1String("KDE"), "kdeglobals"); emitChange(SettingsChanged, SETTINGS_MOUSE); } diff --git a/kcms/input/backends/x11/evdev_settings.h b/kcms/input/backends/x11/evdev_settings.h index 2c286d726..c37adfd2b 100644 --- a/kcms/input/backends/x11/evdev_settings.h +++ b/kcms/input/backends/x11/evdev_settings.h @@ -1,53 +1,50 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EVDEVSETTINGS_H #define EVDEVSETTINGS_H -#include - -class X11Backend; +class X11EvdevBackend; enum class Handed { Right = 0, Left = 1, NotSupported = -1 }; struct EvdevSettings { void save(); - void load(X11Backend *); - void apply(X11Backend *, bool force = false); + void load(X11EvdevBackend *); + void apply(X11EvdevBackend *, bool force = false); bool handedEnabled; bool handedNeedsApply; Handed handed; double accelRate; int thresholdMove; int doubleClickInterval; int dragStartTime; int dragStartDist; bool singleClick; int wheelScrollLines; bool reverseScrollPolarity; - QString currentAccelProfile; }; #endif // EVDEVSETTINGS_H diff --git a/kcms/input/backends/x11/libinput_settings.cpp b/kcms/input/backends/x11/libinput_settings.cpp new file mode 100644 index 000000000..3b040d6e2 --- /dev/null +++ b/kcms/input/backends/x11/libinput_settings.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2018 Roman Gilg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "libinput_settings.h" + +#include +#include + +template<> +bool LibinputSettings::load(QString key, bool defVal) +{ + KSharedConfig::Ptr kcminputPtr = KSharedConfig::openConfig("kcminputrc"); + KConfigGroup group(kcminputPtr, "Mouse"); + + return group.readEntry(key, defVal); +} + +template<> +qreal LibinputSettings::load(QString key, qreal defVal) +{ + KSharedConfig::Ptr kcminputPtr = KSharedConfig::openConfig("kcminputrc"); + KConfigGroup group(kcminputPtr, "Mouse"); + + return group.readEntry(key, defVal); +} + +template<> +void LibinputSettings::save(QString key, bool val) +{ + KSharedConfig::Ptr kcminputPtr = KSharedConfig::openConfig("kcminputrc"); + KConfigGroup group(kcminputPtr, "Mouse"); + + group.writeEntry(key, val); + + group.sync(); + kcminputPtr->sync(); +} + +template<> +void LibinputSettings::save(QString key, qreal val) +{ + KSharedConfig::Ptr kcminputPtr = KSharedConfig::openConfig("kcminputrc"); + KConfigGroup group(kcminputPtr, "Mouse"); + + group.writeEntry(key, val); + + group.sync(); + kcminputPtr->sync(); +} diff --git a/kcms/input/backends/x11/evdev_settings.h b/kcms/input/backends/x11/libinput_settings.h similarity index 54% copy from kcms/input/backends/x11/evdev_settings.h copy to kcms/input/backends/x11/libinput_settings.h index 2c286d726..07214c038 100644 --- a/kcms/input/backends/x11/evdev_settings.h +++ b/kcms/input/backends/x11/libinput_settings.h @@ -1,53 +1,33 @@ /* - * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef EVDEVSETTINGS_H -#define EVDEVSETTINGS_H +#ifndef LIBINPUTSETTINGS_H +#define LIBINPUTSETTINGS_H -#include +#include -class X11Backend; - -enum class Handed { - Right = 0, - Left = 1, - NotSupported = -1 -}; - -struct EvdevSettings +struct LibinputSettings { - void save(); - void load(X11Backend *); - void apply(X11Backend *, bool force = false); + template + T load(QString key, T defVal); - bool handedEnabled; - bool handedNeedsApply; - Handed handed; - double accelRate; - int thresholdMove; - int doubleClickInterval; - int dragStartTime; - int dragStartDist; - bool singleClick; - int wheelScrollLines; - bool reverseScrollPolarity; - QString currentAccelProfile; + template + void save(QString key, T val); }; -#endif // EVDEVSETTINGS_H +#endif // LIBINPUTSETTINGS_H diff --git a/kcms/input/backends/x11/x11_backend.cpp b/kcms/input/backends/x11/x11_backend.cpp index 47aa093f5..a739e899a 100644 --- a/kcms/input/backends/x11/x11_backend.cpp +++ b/kcms/input/backends/x11/x11_backend.cpp @@ -1,498 +1,157 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "x11_backend.h" +#include "x11_evdev_backend.h" +#include "x11_libinput_backend.h" + +#include "logging.h" #include +#include + #include #include #include -#include -#include +#include +#include + +#include #include #include #include #include #include #include #ifdef HAVE_XCURSOR #include #include #endif -static const char PROFILE_NONE[] = I18N_NOOP("None"); -static const char PROFILE_ADAPTIVE[] = I18N_NOOP("Adaptive"); -static const char PROFILE_FLAT[] = I18N_NOOP("Flat"); - -struct ScopedXDeleter { - static inline void cleanup(void* pointer) - { - if (pointer) { - XFree(pointer); - } - } -}; - -template -static void XI2ForallPointerDevices(Display* dpy, const Callback& callback) +X11Backend *X11Backend::implementation(QObject *parent) { - int ndevices_return; - XIDeviceInfo* info = XIQueryDevice(dpy, XIAllDevices, &ndevices_return); - if (!info) { - return; - } - for (int i = 0; i < ndevices_return; ++i) { - if ((info + i)->use == XISlavePointer) { - callback(info + i); - } - } - XIFreeDeviceInfo(info); -} + auto dpy = QX11Info::display(); + Atom testAtom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL, True); -template -static void XIForallPointerDevices(Display* dpy, const Callback& callback) -{ - int ndevices_return; - XDeviceInfo* info = XListInputDevices(dpy, &ndevices_return); - if (!info) { - return; + //There are multiple possible drivers + if (testAtom) { + qCDebug(KCM_INPUT) << "Using libinput driver on X11."; + return new X11LibinputBackend(parent); } - for (int i = 0; i < ndevices_return; ++i) { - if (info[i].use == IsXPointer || info[i].use == IsXExtensionPointer) { - callback(info + i); - } + else { + qCDebug(KCM_INPUT) << "Using evdev driver on X11."; + return new X11EvdevBackend(parent); } - XFreeDeviceList(info); } X11Backend::X11Backend(QObject* parent) : InputBackend(parent) { - m_mode = InputBackendMode::XEvdev; - m_platformX11 = QX11Info::isPlatformX11(); if (m_platformX11) { m_dpy = QX11Info::display(); } else { // TODO: remove this - not needed anymore with Wayland backend! // let's hope we have a compatibility system like Xwayland ready m_dpy = XOpenDisplay(nullptr); } - m_settings = new EvdevSettings(); - initAtom(); -} - -void X11Backend::initAtom() -{ - if (!m_dpy) { - return; - } - - m_libinputAccelProfileAvailableAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, True); - m_libinputAccelProfileEnabledAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True); - m_libinputNaturalScrollAtom = XInternAtom(m_dpy, LIBINPUT_PROP_NATURAL_SCROLL, True); - - m_evdevScrollDistanceAtom = XInternAtom(m_dpy, EVDEV_PROP_SCROLL_DISTANCE, True); - m_evdevWheelEmulationAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL, True); - m_evdevWheelEmulationAxesAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL_AXES, True); - - m_touchpadAtom = XInternAtom(m_dpy, XI_TOUCHPAD, True); } - X11Backend::~X11Backend() { if (!m_platformX11 && m_dpy) { XCloseDisplay(m_dpy); } - delete m_settings; -} - -bool X11Backend::supportScrollPolarity() -{ - return m_numButtons >= 5; -} - -QStringList X11Backend::supportedAccelerationProfiles() -{ - return m_supportedAccelerationProfiles; -} - -QString X11Backend::accelerationProfile() -{ - return m_accelerationProfile; -} - -double X11Backend::accelRate() -{ - return m_accelRate; -} - -Handed X11Backend::handed() -{ - return m_handed; -} - -int X11Backend::threshold() -{ - return m_threshold; -} - -void X11Backend::load() -{ - if (!m_dpy) { - return; - } - - m_accelRate = 1.0; - int accel_num, accel_den; - XGetPointerControl(m_dpy, &accel_num, &accel_den, &m_threshold); - m_accelRate = double(accel_num) / double(accel_den); - - // get settings from X server - unsigned char map[256]; - m_numButtons = XGetPointerMapping(m_dpy, map, 256); - m_middleButton = -1; - - m_handed = Handed::NotSupported; - // ## keep this in sync with KGlobalSettings::mouseSettings - if (m_numButtons == 2) { - if (map[0] == 1 && map[1] == 2) { - m_handed = Handed::Right; - } else if (map[0] == 2 && map[1] == 1) { - m_handed = Handed::Left; - } - } else if (m_numButtons >= 3) { - m_middleButton = map[1]; - if (map[0] == 1 && map[2] == 3) { - m_handed = Handed::Right; - } else if (map[0] == 3 && map[2] == 1) { - m_handed = Handed::Left; - } - } - - m_supportedAccelerationProfiles.clear(); - bool adaptiveAvailable = false; - bool flatAvailable = false; - bool adaptiveEnabled = false; - bool flatEnabled = false; - if (m_libinputAccelProfileAvailableAtom != None && m_libinputAccelProfileEnabledAtom != None) { - XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { - int deviceid = info->deviceid; - Status status; - Atom type_return; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; - - unsigned char *_data = nullptr; - //data returned is an 2 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - QScopedArrayPointer data(_data); - _data = nullptr; - if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { - return; - } - adaptiveAvailable = adaptiveAvailable || data[0]; - flatAvailable = flatAvailable || data[1]; - - //data returned is an 2 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, 0, 2, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - data.reset(_data); - _data = nullptr; - if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { - return; - } - adaptiveEnabled = adaptiveEnabled || data[0]; - flatEnabled = flatEnabled || data[1]; - }); - } - - if (adaptiveAvailable) { - m_supportedAccelerationProfiles << PROFILE_ADAPTIVE; - } - if (flatAvailable) { - m_supportedAccelerationProfiles << PROFILE_FLAT; - } - if (adaptiveAvailable || flatAvailable) { - m_supportedAccelerationProfiles << PROFILE_NONE; - } - - m_accelerationProfile = PROFILE_NONE; - if (adaptiveEnabled) { - m_accelerationProfile = PROFILE_ADAPTIVE; - } else if (flatEnabled) { - m_accelerationProfile = PROFILE_FLAT; - } - - m_settings->load(this); -} - -void X11Backend::apply(bool force) -{ - // 256 might seems extreme, but X has already been known to return 32, - // and we don't want to truncate things. Xlib limits the table to 256 bytes, - // so it's a good upper bound.. - unsigned char map[256]; - XGetPointerMapping(m_dpy, map, 256); - - if (m_settings->handedEnabled && (m_settings->handedNeedsApply || force)) { - if (m_numButtons == 1) { - map[0] = (unsigned char) 1; - } else if (m_numButtons == 2) { - if (m_settings->handed == Handed::Right) { - map[0] = (unsigned char) 1; - map[1] = (unsigned char) 3; - } else { - map[0] = (unsigned char) 3; - map[1] = (unsigned char) 1; - } - } else { // 3 buttons and more - if (m_settings->handed == Handed::Right) { - map[0] = (unsigned char) 1; - map[1] = (unsigned char) m_middleButton; - map[2] = (unsigned char) 3; - } else { - map[0] = (unsigned char) 3; - map[1] = (unsigned char) m_middleButton; - map[2] = (unsigned char) 1; - } - } - - int retval; - if (m_numButtons >= 1) { - while ((retval = XSetPointerMapping(m_dpy, map, - m_numButtons)) == MappingBusy) - /* keep trying until the pointer is free */ - { }; - } - - // apply reverseScrollPolarity for all non-touchpad pointer, touchpad - // are belong to kcm touchpad. - XIForallPointerDevices(m_dpy, [this](XDeviceInfo * info) { - int deviceid = info->id; - if (info->type == m_touchpadAtom) { - return; - } - if (libinputApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity)) { - return; - } - evdevApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity); - }); - - } - - XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { - libinputApplyAccelerationProfile(info->deviceid, m_settings->currentAccelProfile); - }); - - XChangePointerControl(m_dpy, - true, true, int(qRound(m_settings->accelRate * 10)), 10, m_settings->thresholdMove); - - XFlush(m_dpy); } QString X11Backend::currentCursorTheme() { if (!m_dpy) { return QString(); } QByteArray name = XGetDefault(m_dpy, "Xcursor", "theme"); #ifdef HAVE_XCURSOR if (name.isEmpty()) { name = QByteArray(XcursorGetTheme(m_dpy)); } #endif return QFile::decodeName(name); } void X11Backend::applyCursorTheme(const QString& theme, int size) { #ifdef HAVE_XCURSOR // Apply the KDE cursor theme to ourselves if (m_dpy) { return; } if (!theme.isEmpty()) { XcursorSetTheme(m_dpy, QFile::encodeName(theme)); } if (size >= 0) { XcursorSetDefaultSize(m_dpy, size); } // Load the default cursor from the theme and apply it to the root window. Cursor handle = XcursorLibraryLoadCursor(m_dpy, "left_ptr"); XDefineCursor(m_dpy, DefaultRootWindow(m_dpy), handle); XFreeCursor(m_dpy, handle); // Don't leak the cursor XFlush(m_dpy); #endif } -bool X11Backend::evdevApplyReverseScroll(int deviceid, bool reverse) +void X11Backend::kcmInit() { - // Check atom availability first. - if (m_evdevWheelEmulationAtom == None || m_evdevScrollDistanceAtom == None || - m_evdevWheelEmulationAxesAtom == None) { - return false; - } - Status status; - Atom type_return; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; - - unsigned char* _data = nullptr; - //data returned is an 1 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_evdevWheelEmulationAtom, 0, 1, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - QScopedArrayPointer data(_data); - _data = nullptr; - if (status != Success) { - return false; - } + KConfigGroup group = KConfig("kcminputrc", KConfig::NoGlobals).group("Mouse"); + QString theme = group.readEntry("cursorTheme", QString()); + QString size = group.readEntry("cursorSize", QString()); - // pointer device without wheel emulation - if (type_return != XA_INTEGER || data == NULL || *data == False) { - status = XIGetProperty(m_dpy, deviceid, m_evdevScrollDistanceAtom, 0, 3, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - data.reset(_data); - _data = nullptr; - // negate scroll distance - if (status == Success && type_return == XA_INTEGER && - format_return == 32 && num_items_return == 3) { - int32_t* vals = (int32_t*)data.data(); - for (unsigned long i = 0; i < num_items_return; ++i) { - int32_t val = *(vals + i); - *(vals + i) = (int32_t)(reverse ? -abs(val) : abs(val)); - } - XIChangeProperty(m_dpy, deviceid, m_evdevScrollDistanceAtom, XA_INTEGER, - 32, XIPropModeReplace, data.data(), 3); + int intSize = -1; + if (size.isEmpty()) { + bool ok; + uint value = size.toUInt(&ok); + if (ok) { + intSize = value; } - } else { // wheel emulation used, reverse wheel axes - status = XIGetProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, 0, 4, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - data.reset(_data); - _data = nullptr; - if (status == Success && type_return == XA_INTEGER && - format_return == 8 && num_items_return == 4) { - // when scroll direction is not reversed, - // up button id should be smaller than down button id, - // up/left are odd elements, down/right are even elements - for (int i = 0; i < 2; ++i) { - unsigned char odd = data[i * 2]; - unsigned char even = data[i * 2 + 1]; - unsigned char max_elem = std::max(odd, even); - unsigned char min_elem = std::min(odd, even); - data[i * 2] = reverse ? max_elem : min_elem; - data[i * 2 + 1] = reverse ? min_elem : max_elem; - } - XIChangeProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, XA_INTEGER, - 8, XIPropModeReplace, data.data(), 4); - } - } - - return true; -} - -bool X11Backend::libinputApplyReverseScroll(int deviceid, bool reverse) -{ - // Check atom availability first. - if (m_libinputNaturalScrollAtom == None) { - return false; } - Status status; - Atom type_return; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; + // Note: If you update this code, update kapplymousetheme as well. - unsigned char *_data = nullptr; - //data returned is an 1 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, 0, 1, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - if (status != Success) { - return false; + // use a default value for theme only if it's not configured at all, not even in X resources + if (theme.isEmpty() && currentCursorTheme().isEmpty()) { + theme = "breeze_cursors"; } - QScopedArrayPointer data(_data); - _data = nullptr; - if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 1) { - return false; - } - unsigned char natural = reverse ? 1 : 0; - XIChangeProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, XA_INTEGER, - 8, XIPropModeReplace, &natural, 1); - return true; -} + applyCursorTheme(theme, intSize); -void X11Backend::libinputApplyAccelerationProfile(int deviceid, QString profile) -{ - // Check atom availability first. - if (m_libinputAccelProfileAvailableAtom == None || m_libinputAccelProfileEnabledAtom == None) { - return; - } - - unsigned char profileData[2]; - if (profile == PROFILE_NONE) { - profileData[0] = profileData[1] = 0; - } else if (profile == PROFILE_ADAPTIVE) { - profileData[0] = 1; - profileData[1] = 0; - } else if (profile == PROFILE_FLAT) { - profileData[0] = 0; - profileData[1] = 1; - } - Status status; - Atom type_return; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; - - unsigned char *_data = nullptr; - //data returned is an 1 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - if (status != Success) { - return; - } - QScopedArrayPointer data(_data); - _data = nullptr; - if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { - return; + // Tell klauncher to set the XCURSOR_THEME and XCURSOR_SIZE environment + // variables when launching applications. + OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), + QStringLiteral("/KLauncher"), + QDBusConnection::sessionBus()); + if (!theme.isEmpty()) { + klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), theme); } - // Check availability for profile. - if (profileData[0] > data[0] || profileData[1] > data[1]) { - return; + if (!size.isEmpty()) { + klauncher.setLaunchEnv(QStringLiteral("XCURSOR_SIZE"), size); } - XIChangeProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, XA_INTEGER, - 8, XIPropModeReplace, profileData, 2); } diff --git a/kcms/input/backends/x11/x11_backend.h b/kcms/input/backends/x11/x11_backend.h index 2e8fca14e..ed5e39fa9 100644 --- a/kcms/input/backends/x11/x11_backend.h +++ b/kcms/input/backends/x11/x11_backend.h @@ -1,91 +1,51 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef X11BACKEND_H #define X11BACKEND_H #include "inputbackend.h" -#include "evdev_settings.h" #include #include -class ConfigPlugin; - class X11Backend : public InputBackend { Q_OBJECT + public: - X11Backend(QObject *parent = nullptr); + static X11Backend *implementation(QObject *parent = nullptr); ~X11Backend(); - bool isValid() const override { return m_dpy != nullptr; } - - void load() override; - - void apply(bool force = false); + void kcmInit() override; - EvdevSettings* settings() { - return m_settings; - } - - bool supportScrollPolarity(); - QStringList supportedAccelerationProfiles(); - QString accelerationProfile(); - double accelRate(); - Handed handed(); - int threshold(); + bool isValid() const override { return m_dpy != nullptr; } QString currentCursorTheme(); void applyCursorTheme(const QString &name, int size); -Q_SIGNALS: - void mouseStateChanged(); - void mousesChanged(); - void mouseReset(); - -private: - void initAtom(); - bool evdevApplyReverseScroll(int deviceid, bool reverse); - bool libinputApplyReverseScroll(int deviceid, bool reverse); - void libinputApplyAccelerationProfile(int deviceid, QString profile); - - Atom m_libinputAccelProfileAvailableAtom; - Atom m_libinputAccelProfileEnabledAtom; - Atom m_libinputNaturalScrollAtom; - - Atom m_evdevWheelEmulationAtom; - Atom m_evdevScrollDistanceAtom; - Atom m_evdevWheelEmulationAxesAtom; +protected: + X11Backend(QObject *parent = nullptr); - Atom m_touchpadAtom; // We may still need to do something on non-X11 platform due to Xwayland. - Display* m_dpy; + Display* m_dpy = nullptr; bool m_platformX11; - EvdevSettings *m_settings = nullptr; - int m_numButtons = 1; - Handed m_handed = Handed::NotSupported; - double m_accelRate = 1.0; - int m_threshold = 0; - int m_middleButton = -1; - QStringList m_supportedAccelerationProfiles; - QString m_accelerationProfile; }; #endif // X11BACKEND_H diff --git a/kcms/input/backends/x11/x11_backend.cpp b/kcms/input/backends/x11/x11_evdev_backend.cpp similarity index 50% copy from kcms/input/backends/x11/x11_backend.cpp copy to kcms/input/backends/x11/x11_evdev_backend.cpp index 47aa093f5..b1cb43750 100644 --- a/kcms/input/backends/x11/x11_backend.cpp +++ b/kcms/input/backends/x11/x11_evdev_backend.cpp @@ -1,498 +1,279 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "x11_backend.h" +#include "x11_evdev_backend.h" #include #include #include #include #include -#include #include #include #include #include #include #include #ifdef HAVE_XCURSOR #include #include #endif -static const char PROFILE_NONE[] = I18N_NOOP("None"); -static const char PROFILE_ADAPTIVE[] = I18N_NOOP("Adaptive"); -static const char PROFILE_FLAT[] = I18N_NOOP("Flat"); - struct ScopedXDeleter { static inline void cleanup(void* pointer) { if (pointer) { XFree(pointer); } } }; -template -static void XI2ForallPointerDevices(Display* dpy, const Callback& callback) -{ - int ndevices_return; - XIDeviceInfo* info = XIQueryDevice(dpy, XIAllDevices, &ndevices_return); - if (!info) { - return; - } - for (int i = 0; i < ndevices_return; ++i) { - if ((info + i)->use == XISlavePointer) { - callback(info + i); - } - } - XIFreeDeviceInfo(info); -} - template static void XIForallPointerDevices(Display* dpy, const Callback& callback) { int ndevices_return; XDeviceInfo* info = XListInputDevices(dpy, &ndevices_return); if (!info) { return; } for (int i = 0; i < ndevices_return; ++i) { if (info[i].use == IsXPointer || info[i].use == IsXExtensionPointer) { callback(info + i); } } XFreeDeviceList(info); } -X11Backend::X11Backend(QObject* parent) - : InputBackend(parent) +X11EvdevBackend::X11EvdevBackend(QObject* parent) + : X11Backend(parent) { m_mode = InputBackendMode::XEvdev; - - m_platformX11 = QX11Info::isPlatformX11(); - if (m_platformX11) { - m_dpy = QX11Info::display(); - } else { - // TODO: remove this - not needed anymore with Wayland backend! - // let's hope we have a compatibility system like Xwayland ready - m_dpy = XOpenDisplay(nullptr); - } m_settings = new EvdevSettings(); initAtom(); } -void X11Backend::initAtom() +void X11EvdevBackend::initAtom() { if (!m_dpy) { return; } - m_libinputAccelProfileAvailableAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, True); - m_libinputAccelProfileEnabledAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True); - m_libinputNaturalScrollAtom = XInternAtom(m_dpy, LIBINPUT_PROP_NATURAL_SCROLL, True); - m_evdevScrollDistanceAtom = XInternAtom(m_dpy, EVDEV_PROP_SCROLL_DISTANCE, True); m_evdevWheelEmulationAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL, True); m_evdevWheelEmulationAxesAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL_AXES, True); m_touchpadAtom = XInternAtom(m_dpy, XI_TOUCHPAD, True); } -X11Backend::~X11Backend() +X11EvdevBackend::~X11EvdevBackend() { - if (!m_platformX11 && m_dpy) { - XCloseDisplay(m_dpy); - } delete m_settings; } -bool X11Backend::supportScrollPolarity() +bool X11EvdevBackend::supportScrollPolarity() { return m_numButtons >= 5; } -QStringList X11Backend::supportedAccelerationProfiles() -{ - return m_supportedAccelerationProfiles; -} - -QString X11Backend::accelerationProfile() -{ - return m_accelerationProfile; -} - -double X11Backend::accelRate() +double X11EvdevBackend::accelRate() { return m_accelRate; } -Handed X11Backend::handed() +Handed X11EvdevBackend::handed() { return m_handed; } -int X11Backend::threshold() +int X11EvdevBackend::threshold() { return m_threshold; } -void X11Backend::load() +void X11EvdevBackend::load() { if (!m_dpy) { return; } m_accelRate = 1.0; int accel_num, accel_den; XGetPointerControl(m_dpy, &accel_num, &accel_den, &m_threshold); m_accelRate = double(accel_num) / double(accel_den); // get settings from X server unsigned char map[256]; m_numButtons = XGetPointerMapping(m_dpy, map, 256); m_middleButton = -1; m_handed = Handed::NotSupported; // ## keep this in sync with KGlobalSettings::mouseSettings if (m_numButtons == 2) { if (map[0] == 1 && map[1] == 2) { m_handed = Handed::Right; } else if (map[0] == 2 && map[1] == 1) { m_handed = Handed::Left; } } else if (m_numButtons >= 3) { m_middleButton = map[1]; if (map[0] == 1 && map[2] == 3) { m_handed = Handed::Right; } else if (map[0] == 3 && map[2] == 1) { m_handed = Handed::Left; } } - m_supportedAccelerationProfiles.clear(); - bool adaptiveAvailable = false; - bool flatAvailable = false; - bool adaptiveEnabled = false; - bool flatEnabled = false; - if (m_libinputAccelProfileAvailableAtom != None && m_libinputAccelProfileEnabledAtom != None) { - XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { - int deviceid = info->deviceid; - Status status; - Atom type_return; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; - - unsigned char *_data = nullptr; - //data returned is an 2 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - QScopedArrayPointer data(_data); - _data = nullptr; - if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { - return; - } - adaptiveAvailable = adaptiveAvailable || data[0]; - flatAvailable = flatAvailable || data[1]; - - //data returned is an 2 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, 0, 2, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - data.reset(_data); - _data = nullptr; - if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { - return; - } - adaptiveEnabled = adaptiveEnabled || data[0]; - flatEnabled = flatEnabled || data[1]; - }); - } - - if (adaptiveAvailable) { - m_supportedAccelerationProfiles << PROFILE_ADAPTIVE; - } - if (flatAvailable) { - m_supportedAccelerationProfiles << PROFILE_FLAT; - } - if (adaptiveAvailable || flatAvailable) { - m_supportedAccelerationProfiles << PROFILE_NONE; - } - - m_accelerationProfile = PROFILE_NONE; - if (adaptiveEnabled) { - m_accelerationProfile = PROFILE_ADAPTIVE; - } else if (flatEnabled) { - m_accelerationProfile = PROFILE_FLAT; - } - m_settings->load(this); } -void X11Backend::apply(bool force) +void X11EvdevBackend::apply(bool force) { // 256 might seems extreme, but X has already been known to return 32, // and we don't want to truncate things. Xlib limits the table to 256 bytes, // so it's a good upper bound.. unsigned char map[256]; XGetPointerMapping(m_dpy, map, 256); if (m_settings->handedEnabled && (m_settings->handedNeedsApply || force)) { if (m_numButtons == 1) { map[0] = (unsigned char) 1; } else if (m_numButtons == 2) { if (m_settings->handed == Handed::Right) { map[0] = (unsigned char) 1; map[1] = (unsigned char) 3; } else { map[0] = (unsigned char) 3; map[1] = (unsigned char) 1; } } else { // 3 buttons and more if (m_settings->handed == Handed::Right) { map[0] = (unsigned char) 1; map[1] = (unsigned char) m_middleButton; map[2] = (unsigned char) 3; } else { map[0] = (unsigned char) 3; map[1] = (unsigned char) m_middleButton; map[2] = (unsigned char) 1; } } int retval; if (m_numButtons >= 1) { while ((retval = XSetPointerMapping(m_dpy, map, m_numButtons)) == MappingBusy) /* keep trying until the pointer is free */ { }; } // apply reverseScrollPolarity for all non-touchpad pointer, touchpad // are belong to kcm touchpad. XIForallPointerDevices(m_dpy, [this](XDeviceInfo * info) { int deviceid = info->id; if (info->type == m_touchpadAtom) { return; } - if (libinputApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity)) { - return; - } evdevApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity); }); } - XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { - libinputApplyAccelerationProfile(info->deviceid, m_settings->currentAccelProfile); - }); - XChangePointerControl(m_dpy, true, true, int(qRound(m_settings->accelRate * 10)), 10, m_settings->thresholdMove); XFlush(m_dpy); } -QString X11Backend::currentCursorTheme() -{ - if (!m_dpy) { - return QString(); - } - - QByteArray name = XGetDefault(m_dpy, "Xcursor", "theme"); -#ifdef HAVE_XCURSOR - if (name.isEmpty()) { - name = QByteArray(XcursorGetTheme(m_dpy)); - } -#endif - return QFile::decodeName(name); -} - -void X11Backend::applyCursorTheme(const QString& theme, int size) -{ -#ifdef HAVE_XCURSOR - - // Apply the KDE cursor theme to ourselves - if (m_dpy) { - return; - } - if (!theme.isEmpty()) { - XcursorSetTheme(m_dpy, QFile::encodeName(theme)); - } - - if (size >= 0) { - XcursorSetDefaultSize(m_dpy, size); - } - - // Load the default cursor from the theme and apply it to the root window. - Cursor handle = XcursorLibraryLoadCursor(m_dpy, "left_ptr"); - XDefineCursor(m_dpy, DefaultRootWindow(m_dpy), handle); - XFreeCursor(m_dpy, handle); // Don't leak the cursor - XFlush(m_dpy); -#endif -} - -bool X11Backend::evdevApplyReverseScroll(int deviceid, bool reverse) +bool X11EvdevBackend::evdevApplyReverseScroll(int deviceid, bool reverse) { // Check atom availability first. if (m_evdevWheelEmulationAtom == None || m_evdevScrollDistanceAtom == None || m_evdevWheelEmulationAxesAtom == None) { return false; } Status status; Atom type_return; int format_return; unsigned long num_items_return; unsigned long bytes_after_return; unsigned char* _data = nullptr; //data returned is an 1 byte boolean status = XIGetProperty(m_dpy, deviceid, m_evdevWheelEmulationAtom, 0, 1, False, XA_INTEGER, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data); QScopedArrayPointer data(_data); _data = nullptr; if (status != Success) { return false; } // pointer device without wheel emulation if (type_return != XA_INTEGER || data == NULL || *data == False) { status = XIGetProperty(m_dpy, deviceid, m_evdevScrollDistanceAtom, 0, 3, False, XA_INTEGER, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data); data.reset(_data); _data = nullptr; // negate scroll distance if (status == Success && type_return == XA_INTEGER && format_return == 32 && num_items_return == 3) { int32_t* vals = (int32_t*)data.data(); for (unsigned long i = 0; i < num_items_return; ++i) { int32_t val = *(vals + i); *(vals + i) = (int32_t)(reverse ? -abs(val) : abs(val)); } XIChangeProperty(m_dpy, deviceid, m_evdevScrollDistanceAtom, XA_INTEGER, 32, XIPropModeReplace, data.data(), 3); } } else { // wheel emulation used, reverse wheel axes status = XIGetProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, 0, 4, False, XA_INTEGER, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data); data.reset(_data); _data = nullptr; if (status == Success && type_return == XA_INTEGER && format_return == 8 && num_items_return == 4) { // when scroll direction is not reversed, // up button id should be smaller than down button id, // up/left are odd elements, down/right are even elements for (int i = 0; i < 2; ++i) { unsigned char odd = data[i * 2]; unsigned char even = data[i * 2 + 1]; unsigned char max_elem = std::max(odd, even); unsigned char min_elem = std::min(odd, even); data[i * 2] = reverse ? max_elem : min_elem; data[i * 2 + 1] = reverse ? min_elem : max_elem; } XIChangeProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, XA_INTEGER, 8, XIPropModeReplace, data.data(), 4); } } return true; } -bool X11Backend::libinputApplyReverseScroll(int deviceid, bool reverse) -{ - // Check atom availability first. - if (m_libinputNaturalScrollAtom == None) { - return false; - } - Status status; - Atom type_return; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; - - unsigned char *_data = nullptr; - //data returned is an 1 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, 0, 1, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - if (status != Success) { - return false; - } - QScopedArrayPointer data(_data); - _data = nullptr; - if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 1) { - return false; - } - unsigned char natural = reverse ? 1 : 0; - XIChangeProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, XA_INTEGER, - 8, XIPropModeReplace, &natural, 1); - return true; -} - -void X11Backend::libinputApplyAccelerationProfile(int deviceid, QString profile) +void X11EvdevBackend::kcmInit() { - // Check atom availability first. - if (m_libinputAccelProfileAvailableAtom == None || m_libinputAccelProfileEnabledAtom == None) { - return; - } - - unsigned char profileData[2]; - if (profile == PROFILE_NONE) { - profileData[0] = profileData[1] = 0; - } else if (profile == PROFILE_ADAPTIVE) { - profileData[0] = 1; - profileData[1] = 0; - } else if (profile == PROFILE_FLAT) { - profileData[0] = 0; - profileData[1] = 1; - } - Status status; - Atom type_return; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; - - unsigned char *_data = nullptr; - //data returned is an 1 byte boolean - status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, - False, XA_INTEGER, &type_return, &format_return, - &num_items_return, &bytes_after_return, &_data); - if (status != Success) { - return; - } - QScopedArrayPointer data(_data); - _data = nullptr; - if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { - return; - } - // Check availability for profile. - if (profileData[0] > data[0] || profileData[1] > data[1]) { - return; - } - XIChangeProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, XA_INTEGER, - 8, XIPropModeReplace, profileData, 2); + m_settings->load(this); + apply(true); // force + X11Backend::kcmInit(); } diff --git a/kcms/input/backends/x11/x11_backend.h b/kcms/input/backends/x11/x11_evdev_backend.h similarity index 66% copy from kcms/input/backends/x11/x11_backend.h copy to kcms/input/backends/x11/x11_evdev_backend.h index 2e8fca14e..ee0ff15b7 100644 --- a/kcms/input/backends/x11/x11_backend.h +++ b/kcms/input/backends/x11/x11_evdev_backend.h @@ -1,91 +1,79 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef X11BACKEND_H -#define X11BACKEND_H +#ifndef X11EVDEVBACKEND_H +#define X11EVDEVBACKEND_H -#include "inputbackend.h" +#include "x11_backend.h" #include "evdev_settings.h" #include #include -class ConfigPlugin; - -class X11Backend : public InputBackend +class X11EvdevBackend : public X11Backend { Q_OBJECT + public: - X11Backend(QObject *parent = nullptr); - ~X11Backend(); + X11EvdevBackend(QObject *parent = nullptr); + ~X11EvdevBackend(); + + void kcmInit() override; bool isValid() const override { return m_dpy != nullptr; } void load() override; void apply(bool force = false); EvdevSettings* settings() { return m_settings; } bool supportScrollPolarity(); - QStringList supportedAccelerationProfiles(); - QString accelerationProfile(); + double accelRate(); Handed handed(); int threshold(); - QString currentCursorTheme(); - void applyCursorTheme(const QString &name, int size); - Q_SIGNALS: void mouseStateChanged(); void mousesChanged(); void mouseReset(); private: void initAtom(); bool evdevApplyReverseScroll(int deviceid, bool reverse); - bool libinputApplyReverseScroll(int deviceid, bool reverse); - void libinputApplyAccelerationProfile(int deviceid, QString profile); - Atom m_libinputAccelProfileAvailableAtom; - Atom m_libinputAccelProfileEnabledAtom; - Atom m_libinputNaturalScrollAtom; Atom m_evdevWheelEmulationAtom; Atom m_evdevScrollDistanceAtom; Atom m_evdevWheelEmulationAxesAtom; Atom m_touchpadAtom; - // We may still need to do something on non-X11 platform due to Xwayland. - Display* m_dpy; - bool m_platformX11; + EvdevSettings *m_settings = nullptr; int m_numButtons = 1; Handed m_handed = Handed::NotSupported; double m_accelRate = 1.0; int m_threshold = 0; int m_middleButton = -1; - QStringList m_supportedAccelerationProfiles; - QString m_accelerationProfile; }; -#endif // X11BACKEND_H +#endif // X11EVDEVBACKEND_H diff --git a/kcms/input/backends/x11/x11_libinput_backend.cpp b/kcms/input/backends/x11/x11_libinput_backend.cpp new file mode 100644 index 000000000..17f3b0764 --- /dev/null +++ b/kcms/input/backends/x11/x11_libinput_backend.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Roman Gilg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "x11_libinput_backend.h" +#include "x11_libinput_dummydevice.h" + +X11LibinputBackend::X11LibinputBackend(QObject *parent) : + X11Backend(parent) +{ + m_mode = InputBackendMode::XLibinput; + m_device = new X11LibinputDummyDevice(this, m_dpy); +} + +bool X11LibinputBackend::applyConfig() +{ + return static_cast(m_device)->applyConfig(); +} + +bool X11LibinputBackend::getConfig() +{ + return static_cast(m_device)->getConfig(); +} + +bool X11LibinputBackend::getDefaultConfig() +{ + return static_cast(m_device)->getDefaultConfig(); +} + +bool X11LibinputBackend::isChangedConfig() const +{ + return static_cast(m_device)->isChangedConfig(); +} + +void X11LibinputBackend::kcmInit() +{ + getConfig(); + applyConfig(); + X11Backend::kcmInit(); +} diff --git a/kcms/input/backends/x11/x11_libinput_backend.h b/kcms/input/backends/x11/x11_libinput_backend.h new file mode 100644 index 000000000..9343dc779 --- /dev/null +++ b/kcms/input/backends/x11/x11_libinput_backend.h @@ -0,0 +1,54 @@ +/* + * Copyright 2018 Roman Gilg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef X11LIBINPUTBACKEND_H +#define X11LIBINPUTBACKEND_H + +#include "x11_backend.h" + +#include + +class X11LibinputBackend : public X11Backend +{ + Q_OBJECT + + Q_PROPERTY(int deviceCount READ deviceCount CONSTANT) + +public: + explicit X11LibinputBackend(QObject *parent = 0); + ~X11LibinputBackend() = default; + + void kcmInit() override; + + bool applyConfig() override; + bool getConfig() override; + bool getDefaultConfig() override; + bool isChangedConfig() const override; + QString errorString() const override { return m_errorString; } + + virtual int deviceCount() const override { return 1; } + virtual QVector getDevices() const override { + return QVector(1, m_device); + } + +private: + QObject *m_device; + QString m_errorString = QString(); +}; + +#endif // X11LIBINPUTBACKEND_H diff --git a/kcms/input/backends/x11/x11_libinput_dummydevice.cpp b/kcms/input/backends/x11/x11_libinput_dummydevice.cpp new file mode 100644 index 000000000..6efca425c --- /dev/null +++ b/kcms/input/backends/x11/x11_libinput_dummydevice.cpp @@ -0,0 +1,241 @@ +/* + * Copyright 2018 Roman Gilg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "x11_libinput_dummydevice.h" +#include "libinput_settings.h" + +#include + +#include +#include +#include + +template +static void XI2ForallPointerDevices(Display* dpy, const Callback& callback) +{ + int ndevices_return; + XIDeviceInfo* info = XIQueryDevice(dpy, XIAllDevices, &ndevices_return); + if (!info) { + return; + } + for (int i = 0; i < ndevices_return; ++i) { + if ((info + i)->use == XISlavePointer) { + callback(info + i); + } + } + XIFreeDeviceInfo(info); +} + +struct ScopedXDeleter { + static inline void cleanup(void* pointer) + { + if (pointer) { + XFree(pointer); + } + } +}; + +namespace { +template +void valueWriterPart(T val, Atom valAtom, Display *dpy) +{ + Q_UNUSED(val); + Q_UNUSED(valAtom); + Q_UNUSED(dpy); +} + +template<> +void valueWriterPart(bool val, Atom valAtom, Display *dpy) +{ + XI2ForallPointerDevices(dpy, [&] (XIDeviceInfo *info) { + Status status; + Atom type_return; + int format_return; + unsigned long num_items_return; + unsigned long bytes_after_return; + + unsigned char *_data = nullptr; + //data returned is an 1 byte boolean + status = XIGetProperty(dpy, info->deviceid, valAtom, 0, 1, + False, XA_INTEGER, &type_return, &format_return, + &num_items_return, &bytes_after_return, &_data); + if (status != Success) { + return; + } + + QScopedArrayPointer data(_data); + _data = nullptr; + + + if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 1) { + return; + } + + unsigned char sendVal = val ? 1 : 0; + + XIChangeProperty(dpy, info->deviceid, valAtom, XA_INTEGER, + 8, XIPropModeReplace, &sendVal, 1); + + }); +} + +template<> +void valueWriterPart(qreal val, Atom valAtom, Display *dpy) +{ + XI2ForallPointerDevices(dpy, [&] (XIDeviceInfo *info) { + Status status; + Atom float_type = XInternAtom (dpy, "FLOAT", False); + Atom type_return; + int format_return; + unsigned long num_items_return; + unsigned long bytes_after_return; + + unsigned char *_data = nullptr; + //data returned is an 1 byte boolean + status = XIGetProperty(dpy, info->deviceid, valAtom, 0, 1, + False, float_type, &type_return, &format_return, + &num_items_return, &bytes_after_return, &_data); + if (status != Success) { + return; + } + + QScopedArrayPointer data(_data); + _data = nullptr; + + + if (type_return != float_type || !data || format_return != 32 || num_items_return != 1) { + return; + } + + unsigned char buffer[4096]; + float *sendPtr = (float*)buffer; + *sendPtr = val; + + XIChangeProperty(dpy, info->deviceid, valAtom, float_type, + format_return, XIPropModeReplace, buffer, 1); + + }); +} +} + +X11LibinputDummyDevice::X11LibinputDummyDevice(QObject *parent, Display *dpy) + : QObject(parent), + m_settings(new LibinputSettings()), + m_dpy(dpy) +{ + m_leftHanded.atom = XInternAtom(dpy, LIBINPUT_PROP_LEFT_HANDED, True); + m_middleEmulation.atom = XInternAtom(dpy, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED, True); + m_naturalScroll.atom = XInternAtom(dpy, LIBINPUT_PROP_NATURAL_SCROLL, True); + m_pointerAcceleration.atom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL, True); + m_pointerAccelerationProfileFlat.atom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True); + + m_supportsDisableEvents.val = false; + m_enabled.val = true; + m_supportedButtons.val = Qt::LeftButton | Qt::MidButton | Qt::RightButton; + m_supportsLeftHanded.val = true; + m_supportsMiddleEmulation.val = true; + m_middleEmulationEnabledByDefault.val = false; + + m_supportsPointerAcceleration.val = true; + m_defaultPointerAcceleration.val = 0; + + m_supportsPointerAccelerationProfileAdaptive.val = true; + m_supportsPointerAccelerationProfileFlat.val = true; + + m_defaultPointerAccelerationProfileAdaptive.val = true; + m_defaultPointerAccelerationProfileFlat.val = false; + + m_supportsNaturalScroll.val = true; + m_naturalScrollEnabledByDefault.val = false; +} + +X11LibinputDummyDevice::~X11LibinputDummyDevice() +{ + delete m_settings; +} + +bool X11LibinputDummyDevice::getConfig() +{ + auto reset = [this](Prop &prop, bool defVal) { + prop.reset(m_settings->load(prop.cfgName, defVal)); + }; + + reset(m_leftHanded, false); + + reset(m_middleEmulation, false); + reset(m_naturalScroll, false); + reset(m_pointerAccelerationProfileFlat, false); + + m_pointerAccelerationProfileAdaptive.reset(!m_settings->load(m_pointerAccelerationProfileFlat.cfgName, false)); + m_pointerAcceleration.reset(m_settings->load(m_pointerAcceleration.cfgName, 0.)); + + return true; +} + +bool X11LibinputDummyDevice::getDefaultConfig() +{ + m_leftHanded.set(false); + + m_pointerAcceleration.set(m_defaultPointerAcceleration); + m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat); + m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive); + + m_middleEmulation.set(m_middleEmulationEnabledByDefault); + m_naturalScroll.set(m_naturalScrollEnabledByDefault); + + return true; +} + +bool X11LibinputDummyDevice::applyConfig() +{ + valueWriter(m_leftHanded); + valueWriter(m_middleEmulation); + valueWriter(m_naturalScroll); + valueWriter(m_pointerAcceleration); + valueWriter(m_pointerAccelerationProfileFlat); + + return true; +} + +template +bool X11LibinputDummyDevice::valueWriter(Prop &prop) +{ + // Check atom availability first. + if (prop.atom == None) { + return false; + } + + if (prop.val != prop.old) { + m_settings->save(prop.cfgName, prop.val); + } + + valueWriterPart(prop.val, prop.atom, m_dpy); + + prop.old = prop.val; + + return true; +} + +bool X11LibinputDummyDevice::isChangedConfig() const +{ + return m_leftHanded.changed() || + m_pointerAcceleration.changed() || + m_pointerAccelerationProfileFlat.changed() || + m_pointerAccelerationProfileAdaptive.changed() || + m_middleEmulation.changed() || + m_naturalScroll.changed(); +} diff --git a/kcms/input/backends/x11/x11_libinput_dummydevice.h b/kcms/input/backends/x11/x11_libinput_dummydevice.h new file mode 100644 index 000000000..dc825cf02 --- /dev/null +++ b/kcms/input/backends/x11/x11_libinput_dummydevice.h @@ -0,0 +1,273 @@ +/* + * Copyright 2018 Roman Gilg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef X11LIBINPUTDUMMYDEVICE_H +#define X11LIBINPUTDUMMYDEVICE_H + +#include +#include +#include + +#include + +class LibinputSettings; + +class X11LibinputDummyDevice : public QObject +{ + Q_OBJECT + + // + // general + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(bool supportsDisableEvents READ supportsDisableEvents CONSTANT) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + + // + // advanced + Q_PROPERTY(Qt::MouseButtons supportedButtons READ supportedButtons CONSTANT) + + Q_PROPERTY(bool supportsLeftHanded READ supportsLeftHanded CONSTANT) + Q_PROPERTY(bool leftHandedEnabledByDefault READ leftHandedEnabledByDefault CONSTANT) + Q_PROPERTY(bool leftHanded READ isLeftHanded WRITE setLeftHanded NOTIFY leftHandedChanged) + + Q_PROPERTY(bool supportsMiddleEmulation READ supportsMiddleEmulation CONSTANT) + Q_PROPERTY(bool middleEmulationEnabledByDefault READ middleEmulationEnabledByDefault CONSTANT) + Q_PROPERTY(bool middleEmulation READ isMiddleEmulation WRITE setMiddleEmulation NOTIFY middleEmulationChanged) + + // + // acceleration speed and profile + Q_PROPERTY(bool supportsPointerAcceleration READ supportsPointerAcceleration CONSTANT) + Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration NOTIFY pointerAccelerationChanged) + + Q_PROPERTY(bool supportsPointerAccelerationProfileFlat READ supportsPointerAccelerationProfileFlat CONSTANT) + Q_PROPERTY(bool defaultPointerAccelerationProfileFlat READ defaultPointerAccelerationProfileFlat CONSTANT) + Q_PROPERTY(bool pointerAccelerationProfileFlat READ pointerAccelerationProfileFlat WRITE setPointerAccelerationProfileFlat NOTIFY pointerAccelerationProfileChanged) + + Q_PROPERTY(bool supportsPointerAccelerationProfileAdaptive READ supportsPointerAccelerationProfileAdaptive CONSTANT) + Q_PROPERTY(bool defaultPointerAccelerationProfileAdaptive READ defaultPointerAccelerationProfileAdaptive CONSTANT) + Q_PROPERTY(bool pointerAccelerationProfileAdaptive READ pointerAccelerationProfileAdaptive WRITE setPointerAccelerationProfileAdaptive NOTIFY pointerAccelerationProfileChanged) + + // + // scrolling + Q_PROPERTY(bool supportsNaturalScroll READ supportsNaturalScroll CONSTANT) + Q_PROPERTY(bool naturalScrollEnabledByDefault READ naturalScrollEnabledByDefault CONSTANT) + Q_PROPERTY(bool naturalScroll READ isNaturalScroll WRITE setNaturalScroll NOTIFY naturalScrollChanged) + +public: + X11LibinputDummyDevice(QObject *parent, Display *dpy); + ~X11LibinputDummyDevice() override; + + bool getConfig(); + bool getDefaultConfig(); + bool applyConfig(); + bool isChangedConfig() const; + + // + // general + QString name() const { + return m_name.val; + } + QString sysName() const { + return m_sysName.val; + } + bool supportsDisableEvents() const { + return m_supportsDisableEvents.val; + } + void setEnabled(bool enabled) { + m_enabled.set(enabled); + } + bool isEnabled() const { + return m_enabled.val; + } + Qt::MouseButtons supportedButtons() const { + return m_supportedButtons.val; + } + + // + // advanced + bool supportsLeftHanded() const { + return m_supportsLeftHanded.val; + } + bool leftHandedEnabledByDefault() const { + return m_leftHandedEnabledByDefault.val; + } + bool isLeftHanded() const { + return m_leftHanded.val; + } + void setLeftHanded(bool set) { + m_leftHanded.set(set); + } + + bool supportsMiddleEmulation() const { + return m_supportsMiddleEmulation.val; + } + bool middleEmulationEnabledByDefault() const { + return m_middleEmulationEnabledByDefault.val; + } + bool isMiddleEmulation() const { + return m_middleEmulation.val; + } + void setMiddleEmulation(bool set) { + m_middleEmulation.set(set); + } + + // + // acceleration speed and profile + bool supportsPointerAcceleration() const { + return m_supportsPointerAcceleration.val; + } + qreal pointerAcceleration() const { + return m_pointerAcceleration.val; + } + void setPointerAcceleration(qreal acceleration) { + m_pointerAcceleration.set(acceleration); + } + + bool supportsPointerAccelerationProfileFlat() const { + return m_supportsPointerAccelerationProfileFlat.val; + } + bool defaultPointerAccelerationProfileFlat() const { + return m_defaultPointerAccelerationProfileFlat.val; + } + bool pointerAccelerationProfileFlat() const { + return m_pointerAccelerationProfileFlat.val; + } + void setPointerAccelerationProfileFlat(bool set) { + m_pointerAccelerationProfileFlat.set(set); + } + + bool supportsPointerAccelerationProfileAdaptive() const { + return m_supportsPointerAccelerationProfileAdaptive.val; + } + bool defaultPointerAccelerationProfileAdaptive() const { + return m_defaultPointerAccelerationProfileAdaptive.val; + } + bool pointerAccelerationProfileAdaptive() const { + return m_pointerAccelerationProfileAdaptive.val; + } + void setPointerAccelerationProfileAdaptive(bool set) { + m_pointerAccelerationProfileAdaptive.set(set); + } + + // + // scrolling + bool supportsNaturalScroll() const { + return m_supportsNaturalScroll.val; + } + bool naturalScrollEnabledByDefault() const { + return m_naturalScrollEnabledByDefault.val; + } + bool isNaturalScroll() const { + return m_naturalScroll.val; + } + void setNaturalScroll(bool set) { + m_naturalScroll.set(set); + } + +Q_SIGNALS: + void leftHandedChanged(); + void pointerAccelerationChanged(); + void pointerAccelerationProfileChanged(); + void enabledChanged(); + void middleEmulationChanged(); + void naturalScrollChanged(); + +private: + template + struct Prop { + explicit Prop(const QString &_name, const QString &_cfgName = "") + : name(_name), + cfgName(_cfgName) + {} + + 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); + } + + void reset(T newVal) { + val = newVal; + old = newVal; + } + + QString name; + QString cfgName; + + bool avail = true; + T old; + T val; + + Atom atom; + }; + + template + bool valueWriter(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", "XLbInptLeftHanded"); + + Prop m_supportsMiddleEmulation = Prop("supportsMiddleEmulation"); + Prop m_middleEmulationEnabledByDefault = Prop("middleEmulationEnabledByDefault"); + Prop m_middleEmulation = Prop("middleEmulation", "XLbInptMiddleEmulation"); + + // + // acceleration speed and profile + Prop m_supportsPointerAcceleration = Prop("supportsPointerAcceleration"); + Prop m_defaultPointerAcceleration = Prop("defaultPointerAcceleration"); + Prop m_pointerAcceleration = Prop("pointerAcceleration", "XLbInptPointerAcceleration"); + + Prop m_supportsPointerAccelerationProfileFlat = Prop("supportsPointerAccelerationProfileFlat"); + Prop m_defaultPointerAccelerationProfileFlat = Prop("defaultPointerAccelerationProfileFlat"); + Prop m_pointerAccelerationProfileFlat = Prop("pointerAccelerationProfileFlat", "XLbInptAccelProfileFlat"); + + Prop m_supportsPointerAccelerationProfileAdaptive = Prop("supportsPointerAccelerationProfileAdaptive"); + Prop m_defaultPointerAccelerationProfileAdaptive = Prop("defaultPointerAccelerationProfileAdaptive"); + Prop m_pointerAccelerationProfileAdaptive = Prop("pointerAccelerationProfileAdaptive"); + + // + // scrolling + Prop m_supportsNaturalScroll = Prop("supportsNaturalScroll"); + Prop m_naturalScrollEnabledByDefault = Prop("naturalScrollEnabledByDefault"); + Prop m_naturalScroll = Prop("naturalScroll", "XLbInptNaturalScroll"); + + LibinputSettings *m_settings; + Display *m_dpy = nullptr; +}; + +#endif // X11LIBINPUTDUMMYDEVICE_H diff --git a/kcms/input/inputbackend.cpp b/kcms/input/inputbackend.cpp index b56912315..ad33a2c5d 100644 --- a/kcms/input/inputbackend.cpp +++ b/kcms/input/inputbackend.cpp @@ -1,42 +1,42 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "inputbackend.h" #include "backends/x11/x11_backend.h" #include "backends/kwin_wl/kwin_wl_backend.h" #include "logging.h" #include InputBackend *InputBackend::implementation(QObject *parent) { //There are multiple possible backends if (KWindowSystem::isPlatformX11()) { qCDebug(KCM_INPUT) << "Using X11 backend"; - return new X11Backend(parent); + return X11Backend::implementation(parent); } else if (KWindowSystem::isPlatformWayland()) { qCDebug(KCM_INPUT) << "Using KWin+Wayland backend"; return new KWinWaylandBackend(parent); } else { qCCritical(KCM_INPUT) << "Not able to select appropriate backend."; return nullptr; } } diff --git a/kcms/input/inputbackend.h b/kcms/input/inputbackend.h index 8332f34cd..cc12048f8 100644 --- a/kcms/input/inputbackend.h +++ b/kcms/input/inputbackend.h @@ -1,73 +1,75 @@ /* * Copyright 2017 Xuetian Weng * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef INPUTBACKEND_H #define INPUTBACKEND_H #include #include #include class ConfigPlugin; enum class InputBackendMode { KWinWayland = 0, - XLibinput = 1, // TODO + XLibinput = 1, XEvdev = 2 }; class InputBackend : public QObject { Q_OBJECT protected: explicit InputBackend(QObject *parent) : QObject(parent) {} InputBackendMode m_mode; public: static InputBackend *implementation(QObject *parent = nullptr); InputBackendMode mode() { return m_mode; } + virtual void kcmInit() {} + virtual bool isValid() const { return false; } virtual void load() {} virtual bool applyConfig(const QVariantHash &) { return false; } virtual bool getConfig(QVariantHash &) { return false; } virtual bool applyConfig() { return false; } virtual bool getConfig() { return false; } virtual bool getDefaultConfig() { return false; } virtual bool isChangedConfig() const { return false; } virtual QString errorString() const { return QString(); } virtual int deviceCount() const { return 0; } virtual QVector getDevices() const { return QVector(); } Q_SIGNALS: void deviceAdded(bool success); void deviceRemoved(int index); }; #endif // INPUTBACKEND_H diff --git a/kcms/input/kapplymousetheme.cpp b/kcms/input/kapplymousetheme.cpp index 15910baf0..f9117fab5 100644 --- a/kcms/input/kapplymousetheme.cpp +++ b/kcms/input/kapplymousetheme.cpp @@ -1,66 +1,68 @@ /* * main.cpp * * Copyright (c) 1999 Matthias Hoelzer-Kluepfel * Copyright (c) 2005 Lubos Lunak * Copyright (c) 2017 Xuetian Weng * Copyright (c) 2018 Roman Gilg * * Requires the Qt widget libraries, available at no cost at * http://www.troll.no/ * * 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 "backends/x11/x11_backend.h" #include #include #include #include int main( int argc, char* argv[] ) { int ret = 0; QGuiApplication app(argc, argv); if( argc != 3 ) return 1; QString theme = QFile::decodeName(argv[ 1 ]); QString size = QFile::decodeName(argv[ 2 ]); if (!KWindowSystem::isPlatformX11()) { qDebug() << "X11 backend not detected. Exit."; return 2; } - X11Backend backend; + X11Backend *backend = X11Backend::implementation(); - if (!backend.isValid()) { + if (!backend->isValid()) { return 2; } // Note: If you update this code, update main.cpp as well. // use a default value for theme only if it's not configured at all, not even in X resources - if(theme.isEmpty() && backend.currentCursorTheme().isEmpty()) + if(theme.isEmpty() && backend->currentCursorTheme().isEmpty()) { theme = "breeze_cursors"; ret = 10; // means to switch to default } - backend.applyCursorTheme(theme, size.toInt()); + backend->applyCursorTheme(theme, size.toInt()); + + delete backend; return ret; } diff --git a/kcms/input/kcm/configcontainer.cpp b/kcms/input/kcm/configcontainer.cpp index f6f082d9d..d50161e47 100644 --- a/kcms/input/kcm/configcontainer.cpp +++ b/kcms/input/kcm/configcontainer.cpp @@ -1,75 +1,75 @@ /* * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "configcontainer.h" #include "configplugin.h" -#include "kcm/libinput/libinput_config.h" -#include "kcm/xlib/xlib_config.h" + +#include "inputbackend.h" #include extern "C" { Q_DECL_EXPORT void kcminit_mouse() { - if (KWindowSystem::isPlatformX11()) { - XlibConfig::kcmInit(); - } + InputBackend *backend = InputBackend::implementation(); + backend->kcmInit(); + delete backend; } } ConfigContainer::ConfigContainer(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { m_plugin = ConfigPlugin::implementation(this); } QSize ConfigContainer::minimumSizeHint() const { return m_plugin->minimumSizeHint(); } QSize ConfigContainer::sizeHint() const { return m_plugin->sizeHint(); } void ConfigContainer::resizeEvent(QResizeEvent *event) { Q_EMIT changed(false); m_plugin->resize(this->size()); } void ConfigContainer::load() { m_plugin->load(); } void ConfigContainer::save() { m_plugin->save(); } void ConfigContainer::defaults() { m_plugin->defaults(); } void ConfigContainer::hideEvent(QHideEvent *e) { m_plugin->hideEvent(e); KCModule::hideEvent(e); } diff --git a/kcms/input/kcm/libinput/libinput_config.cpp b/kcms/input/kcm/libinput/libinput_config.cpp index d93804bc7..bb63f6e32 100644 --- a/kcms/input/kcm/libinput/libinput_config.cpp +++ b/kcms/input/kcm/libinput/libinput_config.cpp @@ -1,225 +1,230 @@ /* * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "libinput_config.h" #include "../configcontainer.h" #include #include #include #include #include #include #include #include #include #include #include "inputbackend.h" static QVariant getDeviceList(InputBackend *backend) { return QVariant::fromValue(backend->getDevices().toList()); } LibinputConfig::LibinputConfig(ConfigContainer *parent, InputBackend *backend) : ConfigPlugin(parent) { m_backend = backend; KAboutData* data = new KAboutData(QStringLiteral("kcmmouse"), i18n("Pointer device KCM"), QStringLiteral("1.0"), i18n("System Settings module for managing mice and trackballs."), KAboutLicense::GPL_V2, i18n("Copyright 2018 Roman Gilg"), QString()); data->addAuthor(i18n("Roman Gilg"), i18n("Developer"), QStringLiteral("subdiff@gmail.com")); m_parent->setAboutData(data); m_initError = !m_backend->errorString().isNull(); m_view = new QQuickWidget(this); m_errorMessage = new KMessageWidget(this); m_errorMessage->setCloseButtonVisible(false); m_errorMessage->setWordWrap(true); m_errorMessage->setVisible(false); QVBoxLayout *layout = new QVBoxLayout(parent); layout->addWidget(m_errorMessage); layout->addWidget(m_view); parent->setLayout(layout); m_view->setResizeMode(QQuickWidget::SizeRootObjectToView); m_view->setClearColor(Qt::transparent); m_view->setAttribute(Qt::WA_AlwaysStackOnTop); m_view->rootContext()->setContextProperty("backend", m_backend); m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(m_view->engine()); kdeclarative.setupBindings(); - m_view->setSource(QUrl("qrc:/libinput/main.qml")); + + if (m_backend->mode() == InputBackendMode::XLibinput) { + m_view->setSource(QUrl("qrc:/libinput/main_deviceless.qml")); + } else { + m_view->setSource(QUrl("qrc:/libinput/main.qml")); + } if (m_initError) { m_errorMessage->setMessageType(KMessageWidget::Error); m_errorMessage->setText(m_backend->errorString()); QMetaObject::invokeMethod(m_errorMessage, "animatedShow", Qt::QueuedConnection); } else { connect(m_backend, SIGNAL(deviceAdded(bool)), this, SLOT(onDeviceAdded(bool))); connect(m_backend, SIGNAL(deviceRemoved(int)), this, SLOT(onDeviceRemoved(int))); connect(m_view->rootObject(), SIGNAL(changeSignal()), this, SLOT(onChange())); } m_view->show(); } QSize LibinputConfig::sizeHint() const { return QQmlProperty::read(m_view->rootObject(), "sizeHint").toSize(); } QSize LibinputConfig::minimumSizeHint() const { return QQmlProperty::read(m_view->rootObject(), "minimumSizeHint").toSize(); } void LibinputConfig::load() { // in case of critical init error in backend, don't try if (m_initError) { return; } if (!m_backend->getConfig()) { m_errorMessage->setMessageType(KMessageWidget::Error); m_errorMessage->setText(i18n("Error while loading values. See logs for more informations. Please restart this configuration module.")); m_errorMessage->animatedShow(); } else { if (!m_backend->deviceCount()) { m_errorMessage->setMessageType(KMessageWidget::Information); m_errorMessage->setText(i18n("No pointer device found. Connect now.")); m_errorMessage->animatedShow(); } } QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend"); } void LibinputConfig::save() { if (!m_backend->applyConfig()) { m_errorMessage->setMessageType(KMessageWidget::Error); m_errorMessage->setText(i18n("Not able to save all changes. See logs for more informations. Please restart this configuration module and try again.")); m_errorMessage->animatedShow(); } else { hideErrorMessage(); } // load newly written values load(); // in case of error, config still in changed state emit m_parent->changed(m_backend->isChangedConfig()); } void LibinputConfig::defaults() { // in case of critical init error in backend, don't try if (m_initError) { return; } if (!m_backend->getDefaultConfig()) { m_errorMessage->setMessageType(KMessageWidget::Error); m_errorMessage->setText(i18n("Error while loading default values. Failed to set some options to their default values.")); m_errorMessage->animatedShow(); } QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend"); emit m_parent->changed(m_backend->isChangedConfig()); } void LibinputConfig::onChange() { if (!m_backend->deviceCount()) { return; } hideErrorMessage(); emit m_parent->changed(m_backend->isChangedConfig()); } void LibinputConfig::onDeviceAdded(bool success) { QQuickItem *rootObj = m_view->rootObject(); if (!success) { m_errorMessage->setMessageType(KMessageWidget::Error); m_errorMessage->setText(i18n("Error while adding newly connected device. Please reconnect it and restart this configuration module.")); } int activeIndex; if (m_backend->deviceCount() == 1) { // if no pointer device was connected previously, show the new device and hide the no-device-message activeIndex = 0; hideErrorMessage(); } else { activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt(); } m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); QMetaObject::invokeMethod(rootObj, "resetModel", Q_ARG(QVariant, activeIndex)); QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); } void LibinputConfig::onDeviceRemoved(int index) { QQuickItem *rootObj = m_view->rootObject(); int activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt(); if (activeIndex == index) { m_errorMessage->setMessageType(KMessageWidget::Information); if (m_backend->deviceCount()) { m_errorMessage->setText(i18n("Pointer device disconnected. Closed its setting dialog.")); } else { m_errorMessage->setText(i18n("Pointer device disconnected. No other devices found.")); } m_errorMessage->animatedShow(); activeIndex = 0; } else { if (index < activeIndex) { activeIndex--; } } m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend)); QMetaObject::invokeMethod(m_view->rootObject(), "resetModel", Q_ARG(QVariant, activeIndex)); QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend"); emit m_parent->changed(m_backend->isChangedConfig()); } void LibinputConfig::hideErrorMessage() { if (m_errorMessage->isVisible()) { m_errorMessage->animatedHide(); } } diff --git a/kcms/input/kcm/libinput/main_deviceless.qml b/kcms/input/kcm/libinput/main_deviceless.qml new file mode 100644 index 000000000..a011d7e29 --- /dev/null +++ b/kcms/input/kcm/libinput/main_deviceless.qml @@ -0,0 +1,251 @@ +/* + * Copyright 2018 Roman Gilg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import QtQuick.Controls 1.4 as Controls +import QtQuick.Layouts 1.3 as Layouts +import QtQuick.Controls.Styles 1.4 as Styles + +import org.kde.plasma.core 2.0 as PlasmaCore + +import "components" + +Item { + id: root + + property size sizeHint: Qt.size(maincol.width, Math.round(1.1 * maincol.height)) + property size minimumSizeHint: Qt.size(maincol.width, Math.round(1.1 * maincol.height)) + signal changeSignal() + + property QtObject device: deviceModel[0] + + property bool loading: false + + function resetModel(index) { + // not implemented + } + + function syncValuesFromBackend() { + loading = true + + leftHanded.load() + accelSpeed.load() + accelProfile.load() + + naturalScroll.load() + + loading = false + } + + Controls.ScrollView { + anchors.fill: parent + + Layouts.ColumnLayout { + id: maincol + spacing: units.largeSpacing + + Column { + spacing: units.smallSpacing * 2 + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.Label { + text: i18n("General settings:") + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.CheckBox { + id: leftHanded + text: i18n("Left handed mode") + + function load() { + if (!maincol.enabled) { + checked = false + return + } + enabled = device.supportsLeftHanded + checked = enabled && device.leftHanded + } + + onCheckedChanged: { + if (enabled && !root.loading) { + device.leftHanded = checked + root.changeSignal() + } + } + + ToolTip { + text: i18n("Swap left and right buttons.") + } + } + + Controls.CheckBox { + id: middleEmulation + text: i18n("Emulate middle button") + + function load() { + if (!maincol.enabled) { + checked = false + return + } + enabled = device.supportsMiddleEmulation + checked = enabled && device.middleEmulation + } + + onCheckedChanged: { + if (enabled && !root.loading) { + device.middleEmulation = checked + root.changeSignal() + } + } + + ToolTip { + text: i18n("Clicking left and right button simultaneously sends middle button click.") + } + } + } + } + } + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.Label { + text: i18n("Acceleration:") + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing * 2 + + Row { + Controls.Slider { + id: accelSpeed + anchors.verticalCenter: parent.verticalCenter + + tickmarksEnabled: true + + minimumValue: 1 + maximumValue: 10 + stepSize: 1 + + implicitWidth: units.gridUnit * 9 + + function load() { + enabled = device.supportsPointerAcceleration + if (!enabled) { + value = 0.1 + return + } + // transform libinput's pointer acceleration range [-1, 1] to slider range [1, 10] + value = 4.5 * device.pointerAcceleration + 5.5 + } + + onValueChanged: { + if (device != undefined && enabled && !root.loading) { + // transform slider range [1, 10] to libinput's pointer acceleration range [-1, 1] + device.pointerAcceleration = Math.round( (value - 5.5) / 4.5 * 100 ) / 100 + root.changeSignal() + } + } + } + } + + ExclGroupBox { + id: accelProfile + label: i18n("Acceleration Profile:") + model: [i18n("Flat"), i18n("Adaptive")] + + function load() { + enabled = device.supportsPointerAccelerationProfileAdaptive + + if (!enabled) { + itemAt(0).checked = false + itemAt(1).checked = false + return + } + + itemAt(0).tooltiptext = i18n("Cursor moves the same distance as finger.") + itemAt(1).tooltiptext = i18n("Cursor travel distance depends on movement speed of finger.") + + var toCheck = device.pointerAccelerationProfileAdaptive ? 1 : 0 + itemAt(toCheck).checked = true + } + + onCurrentChanged: { + if (enabled && !root.loading) { + device.pointerAccelerationProfileFlat = itemAt(0).checked + device.pointerAccelerationProfileAdaptive = itemAt(1).checked + root.changeSignal() + } + } + } + } + } + } + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + Controls.Label { + text: i18n("Scrolling:") + } + + Column { + leftPadding: units.smallSpacing + Column { + spacing: units.smallSpacing + + Controls.CheckBox { + id: naturalScroll + text: i18n("Invert scroll direction") + + function load() { + enabled = device.supportsNaturalScroll + checked = enabled && device.naturalScroll + } + + onCheckedChanged: { + if (enabled && !root.loading) { + device.naturalScroll = checked + root.changeSignal() + } + } + + ToolTip { + text: i18n("Touchscreen like scrolling.") + } + } + } + } + } + } + } + } + } +} diff --git a/kcms/input/kcm/resources.qrc b/kcms/input/kcm/resources.qrc index 8f032c7ef..af2effbd7 100644 --- a/kcms/input/kcm/resources.qrc +++ b/kcms/input/kcm/resources.qrc @@ -1,7 +1,8 @@ libinput/main.qml + libinput/main_deviceless.qml libinput/components/ExclGroupBox.qml libinput/components/ToolTip.qml diff --git a/kcms/input/kcm/xlib/kcmmouse.ui b/kcms/input/kcm/xlib/kcmmouse.ui index 2f0c897aa..d4357f7d0 100644 --- a/kcms/input/kcm/xlib/kcmmouse.ui +++ b/kcms/input/kcm/xlib/kcmmouse.ui @@ -1,647 +1,627 @@ KCMMouse 0 0 653 452 <h1>Mouse</h1> This module allows you to choose various options for the way in which your pointing device works. Your pointing device may be a mouse, trackball, or some other hardware that performs a similar function. 0 0 0 0 0 &General 0 0 If you are left-handed, you may prefer to swap the functions of the left and right buttons on your pointing device by choosing the 'left-handed' option. If your pointing device has more than two buttons, only those that function as the left and right buttons are affected. For example, if you have a three-button mouse, the middle button is unaffected. Button Order true Righ&t handed true handedGroup Le&ft handed handedGroup Qt::Vertical QSizePolicy::Expanding 1 1 Change the direction of scrolling for the mouse wheel or the 4th and 5th mouse buttons. Re&verse scroll direction 0 0 96 80 true Icons true The default behavior in KDE is to select and activate icons with a single click of the left button on your pointing device. This behavior is consistent with what you would expect when you click links in most web browsers. If you would prefer to select with a single click, and activate with a double click, check this option. Dou&ble-click to open files and folders (select icons on first click) false clickGroup Activates and opens a file or folder with a single click. &Single-click to open files and folders true clickGroup Qt::Vertical QSizePolicy::Expanding 20 146 Advanced - + Pointer acceleration: accel - + Pointer threshold: thresh - + Double click interval: doubleClickInterval - + Drag start time: dragStartTime - + Drag start distance: dragStartDist - + Mouse wheel scrolls by: wheelScrollLines - + 0 0 <p>This option allows you to change the relationship between the distance that the mouse pointer moves on the screen and the relative movement of the physical device itself (which may be a mouse, trackball, or some other pointing device.)</p><p> A high value for the acceleration will lead to large movements of the mouse pointer on the screen even when you only make a small movement with the physical device. Selecting very high values may result in the mouse pointer flying across the screen, making it hard to control.</p> x 1 0.100000000000000 20.000000000000000 0.100000000000000 2.000000000000000 - + 0 0 <p>The threshold is the smallest distance that the mouse pointer must move on the screen before acceleration has any effect. If the movement is smaller than the threshold, the mouse pointer moves as if the acceleration was set to 1X;</p><p> thus, when you make small movements with the physical device, there is no acceleration at all, giving you a greater degree of control over the mouse pointer. With larger movements of the physical device, you can move the mouse pointer rapidly to different areas on the screen.</p> 20 2 - + 0 0 The double click interval is the maximal time (in milliseconds) between two mouse clicks which turns them into a double click. If the second click happens later than this time interval after the first click, they are recognized as two separate clicks. msec 100 2000 100 2000 - + 0 0 If you click with the mouse (e.g. in a multi-line editor) and begin to move the mouse within the drag start time, a drag operation will be initiated. msec 100 2000 100 - + 0 0 If you click with the mouse and begin to move the mouse at least the drag start distance, a drag operation will be initiated. 1 20 20 - + 0 0 If you use the wheel of a mouse, this value determines the number of lines to scroll for each wheel movement. Note that if this number exceeds the number of visible lines, it will be ignored and the wheel movement will be handled as a page up/down movement. 1 12 3 - - - - Acceleration Profile: - - - accelProfileComboBox - - - - - - - - 0 - 0 - - - - Qt::Horizontal 40 20 Keyboard Navigation &Move pointer with keyboard (using the num pad) 0 0 msec 1 1000 50 &Acceleration delay: mk_delay R&epeat interval: mk_interval 0 0 msec 1 1000 10 Acceleration &time: mk_time_to_max 0 0 msec 100 10000 200 Ma&ximum speed: mk_max_speed 0 0 pixel/sec 1 2000 20 Acceleration &profile: mk_curve 0 0 -1000 1000 100 Qt::Horizontal 40 20 diff --git a/kcms/input/kcm/xlib/xlib_config.cpp b/kcms/input/kcm/xlib/xlib_config.cpp index 14ad3446c..afbcb1db1 100644 --- a/kcms/input/kcm/xlib/xlib_config.cpp +++ b/kcms/input/kcm/xlib/xlib_config.cpp @@ -1,397 +1,331 @@ /* * Copyright 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca * Copyright 1999 Dirk A. Mueller * Copyright 1999 Matthias Hoelzer-Kluepfel * Copyright 2000 David Faure * Copyright 2000 Bernd Gehrmann * Copyright 2000 Rik Hemsley * Copyright 2000 Brad Hughes * Copyright 2001 Ralf Nolden * Copyright 2004 Brad Hards * Copyright 2018 Roman Gilg * * 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 "xlib_config.h" -#include "backends/x11/x11_backend.h" +#include "backends/x11/x11_evdev_backend.h" #include "../configcontainer.h" #include "../../../migrationlib/kdelibs4config.h" #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include -#include - #include #include -void XlibConfig::kcmInit() -{ - X11Backend *backend = dynamic_cast(InputBackend::implementation()); - if (!backend) { - return; - } - - backend->settings()->load(backend); - backend->settings()->apply(backend, true); // force - - KConfigGroup group = KConfig("kcminputrc", KConfig::NoGlobals).group("Mouse"); - QString theme = group.readEntry("cursorTheme", QString()); - QString size = group.readEntry("cursorSize", QString()); - if (backend) { - int intSize = -1; - if (size.isEmpty()) { - bool ok; - uint value = size.toUInt(&ok); - if (ok) { - intSize = value; - } - } - // Note: If you update this code, update kapplymousetheme as well. - - // use a default value for theme only if it's not configured at all, not even in X resources - if (theme.isEmpty() && backend->currentCursorTheme().isEmpty()) { - theme = "breeze_cursors"; - } - backend->applyCursorTheme(theme, intSize); - } - - // Tell klauncher to set the XCURSOR_THEME and XCURSOR_SIZE environment - // variables when launching applications. - OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), - QStringLiteral("/KLauncher"), - QDBusConnection::sessionBus()); - if (!theme.isEmpty()) { - klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), theme); - } - if (!size.isEmpty()) { - klauncher.setLaunchEnv(QStringLiteral("XCURSOR_SIZE"), size); - } -} - XlibConfig::XlibConfig(ConfigContainer *parent, InputBackend *backend) : ConfigPlugin(parent), - m_backend(dynamic_cast(backend)) + m_backend(dynamic_cast(backend)) { setupUi(this); handedGroup->setId(rightHanded, static_cast(Handed::Right)); handedGroup->setId(leftHanded, static_cast(Handed::Left)); connect(handedGroup, SIGNAL(buttonClicked(int)), m_parent, SLOT(changed())); connect(handedGroup, SIGNAL(buttonClicked(int)), this, SLOT(slotHandedChanged(int))); connect(doubleClick, SIGNAL(clicked()), m_parent, SLOT(changed())); connect(singleClick, SIGNAL(clicked()), m_parent, SLOT(changed())); connect(cbScrollPolarity, SIGNAL(clicked()), m_parent, SLOT(changed())); connect(cbScrollPolarity, SIGNAL(clicked()), this, SLOT(slotScrollPolarityChanged())); connect(accel, SIGNAL(valueChanged(double)), m_parent, SLOT(changed())); connect(thresh, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(thresh, SIGNAL(valueChanged(int)), this, SLOT(slotThreshChanged(int))); - connect(accelProfileComboBox, SIGNAL(currentIndexChanged(int)), m_parent, SLOT(changed())); slotThreshChanged(thresh->value()); // It would be nice if the user had a test field. // Selecting such values in milliseconds is not intuitive connect(doubleClickInterval, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(dragStartTime, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(dragStartDist, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(dragStartDist, SIGNAL(valueChanged(int)), this, SLOT(slotDragStartDistChanged(int))); slotDragStartDistChanged(dragStartDist->value()); connect(wheelScrollLines, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(wheelScrollLines, SIGNAL(valueChanged(int)), SLOT(slotWheelScrollLinesChanged(int))); slotWheelScrollLinesChanged(wheelScrollLines->value()); connect(mouseKeys, SIGNAL(clicked()), this, SLOT(checkAccess())); connect(mouseKeys, SIGNAL(clicked()), m_parent, SLOT(changed())); connect(mk_delay, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(mk_interval, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(mk_time_to_max, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(mk_max_speed, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); connect(mk_curve, SIGNAL(valueChanged(int)), m_parent, SLOT(changed())); KAboutData* about = new KAboutData(QStringLiteral("kcmmouse"), i18n("Mouse"), QStringLiteral("1.0"), QString(), KAboutLicense::GPL, i18n("(c) 1997 - 2018 Mouse developers")); about->addAuthor(i18n("Patrick Dowler")); about->addAuthor(i18n("Dirk A. Mueller")); about->addAuthor(i18n("David Faure")); about->addAuthor(i18n("Bernd Gehrmann")); about->addAuthor(i18n("Rik Hemsley")); about->addAuthor(i18n("Brad Hughes")); about->addAuthor(i18n("Ralf Nolden")); about->addAuthor(i18n("Brad Hards")); about->addAuthor(i18n("Roman Gilg")); m_parent->setAboutData(about); } void XlibConfig::checkAccess() { mk_delay->setEnabled(mouseKeys->isChecked()); mk_interval->setEnabled(mouseKeys->isChecked()); mk_time_to_max->setEnabled(mouseKeys->isChecked()); mk_max_speed->setEnabled(mouseKeys->isChecked()); mk_curve->setEnabled(mouseKeys->isChecked()); } double XlibConfig::getAccel() { return accel->value(); } void XlibConfig::setAccel(double val) { accel->setValue(val); } int XlibConfig::getThreshold() { return thresh->value(); } void XlibConfig::setThreshold(int val) { thresh->setValue(val); } Handed XlibConfig::getHandedness() { if (rightHanded->isChecked()) return Handed::Right; else return Handed::Left; } void XlibConfig::setHandedness(Handed val) { rightHanded->setChecked(false); leftHanded->setChecked(false); if (val == Handed::Right) { rightHanded->setChecked(true); mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_rh.png")); } else { leftHanded->setChecked(true); mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_lh.png")); } m_backend->settings()->handedNeedsApply = true; } - - - void XlibConfig::load() { EvdevSettings *settings = m_backend->settings(); m_parent->kcmLoad(); m_backend->load(); // Only allow setting reversing scroll polarity if we have scroll buttons if (m_backend) { if (m_backend->supportScrollPolarity()) { cbScrollPolarity->setEnabled(true); cbScrollPolarity->show(); } else { cbScrollPolarity->setEnabled(false); cbScrollPolarity->hide(); } } - auto accelerationProfiles = m_backend->supportedAccelerationProfiles(); - accelProfileComboBox->setEnabled(!accelerationProfiles.isEmpty()); - accelProfileComboBox->setVisible(!accelerationProfiles.isEmpty()); - accelerationProfileLabel->setEnabled(!accelerationProfiles.isEmpty()); - accelerationProfileLabel->setVisible(!accelerationProfiles.isEmpty()); - accelProfileComboBox->clear(); - int idx = 0; - for (const auto &profile : accelerationProfiles) { - accelProfileComboBox->addItem(i18n(profile.toUtf8().constData()), profile); - if (profile == settings->currentAccelProfile) { - accelProfileComboBox->setCurrentIndex(idx); - } - idx++; - } - rightHanded->setEnabled(settings->handedEnabled); leftHanded->setEnabled(settings->handedEnabled); if (cbScrollPolarity->isEnabled()) cbScrollPolarity->setEnabled(settings->handedEnabled); cbScrollPolarity->setChecked(settings->reverseScrollPolarity); setAccel(settings->accelRate); setThreshold(settings->thresholdMove); setHandedness(settings->handed); doubleClickInterval->setValue(settings->doubleClickInterval); dragStartTime->setValue(settings->dragStartTime); dragStartDist->setValue(settings->dragStartDist); wheelScrollLines->setValue(settings->wheelScrollLines); singleClick->setChecked(settings->singleClick); doubleClick->setChecked(!settings->singleClick); KConfig ac("kaccessrc"); KConfigGroup group = ac.group("Mouse"); mouseKeys->setChecked(group.readEntry("MouseKeys", false)); mk_delay->setValue(group.readEntry("MKDelay", 160)); int interval = group.readEntry("MKInterval", 5); mk_interval->setValue(interval); // Default time to reach maximum speed: 5000 msec int time_to_max = group.readEntry("MKTimeToMax", (5000+interval/2)/interval); time_to_max = group.readEntry("MK-TimeToMax", time_to_max*interval); mk_time_to_max->setValue(time_to_max); // Default maximum speed: 1000 pixels/sec // (The old default maximum speed from KDE <= 3.4 // (100000 pixels/sec) was way too fast) long max_speed = group.readEntry("MKMaxSpeed", interval); max_speed = max_speed * 1000 / interval; if (max_speed > 2000) max_speed = 2000; max_speed = group.readEntry("MK-MaxSpeed", int(max_speed)); mk_max_speed->setValue(max_speed); mk_curve->setValue(group.readEntry("MKCurve", 0)); checkAccess(); emit m_parent->changed(false); } void XlibConfig::save() { EvdevSettings *settings = m_backend->settings(); settings->accelRate = getAccel(); settings->thresholdMove = getThreshold(); settings->handed = getHandedness(); settings->doubleClickInterval = doubleClickInterval->value(); settings->dragStartTime = dragStartTime->value(); settings->dragStartDist = dragStartDist->value(); settings->wheelScrollLines = wheelScrollLines->value(); settings->singleClick = !doubleClick->isChecked(); settings->reverseScrollPolarity = cbScrollPolarity->isChecked(); - settings->currentAccelProfile = accelProfileComboBox->itemData(accelProfileComboBox->currentIndex()).toString(); m_backend->apply(); settings->save(); KConfig ac("kaccessrc"); KConfigGroup group = ac.group("Mouse"); int interval = mk_interval->value(); group.writeEntry("MouseKeys", mouseKeys->isChecked()); group.writeEntry("MKDelay", mk_delay->value()); group.writeEntry("MKInterval", interval); group.writeEntry("MK-TimeToMax", mk_time_to_max->value()); group.writeEntry("MKTimeToMax", (mk_time_to_max->value() + interval/2)/interval); group.writeEntry("MK-MaxSpeed", mk_max_speed->value()); group.writeEntry("MKMaxSpeed", (mk_max_speed->value()*interval + 500)/1000); group.writeEntry("MKCurve", mk_curve->value()); group.sync(); // restart kaccess KToolInvocation::startServiceByDesktopName("kaccess"); emit m_parent->changed(false); } void XlibConfig::defaults() { setThreshold(2); setAccel(2); setHandedness(Handed::Right); cbScrollPolarity->setChecked(false); doubleClickInterval->setValue(400); dragStartTime->setValue(500); dragStartDist->setValue(4); wheelScrollLines->setValue(3); doubleClick->setChecked(!KDE_DEFAULT_SINGLECLICK); singleClick->setChecked(KDE_DEFAULT_SINGLECLICK); mouseKeys->setChecked(false); mk_delay->setValue(160); mk_interval->setValue(5); mk_time_to_max->setValue(5000); mk_max_speed->setValue(1000); mk_curve->setValue(0); checkAccess(); m_parent->kcmDefaults(); m_parent->changed(true); } /** No descriptions */ void XlibConfig::slotHandedChanged(int val) { if (val == static_cast(Handed::Right)) mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_rh.png")); else mousePix->setPixmap(KStandardDirs::locate("data", "kcminput/pics/mouse_lh.png")); m_backend->settings()->handedNeedsApply = true; } void XlibConfig::slotThreshChanged(int value) { thresh->setSuffix(i18np(" pixel", " pixels", value)); } void XlibConfig::slotDragStartDistChanged(int value) { dragStartDist->setSuffix(i18np(" pixel", " pixels", value)); } void XlibConfig::slotWheelScrollLinesChanged(int value) { wheelScrollLines->setSuffix(i18np(" line", " lines", value)); } void XlibConfig::slotScrollPolarityChanged() { m_backend->settings()->handedNeedsApply = true; } #include "xlib_config.moc" diff --git a/kcms/input/kcm/xlib/xlib_config.h b/kcms/input/kcm/xlib/xlib_config.h index 3fb70b1fb..1895ba2bd 100644 --- a/kcms/input/kcm/xlib/xlib_config.h +++ b/kcms/input/kcm/xlib/xlib_config.h @@ -1,74 +1,74 @@ /* * Copyright 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca * Copyright 1999 Dirk A. Mueller * Copyright 2000 David Faure * Copyright 2018 Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef XLIBCONFIG_H #define XLIBCONFIG_H #include "../configplugin.h" #include "backends/x11/evdev_settings.h" #include "ui_kcmmouse.h" #include #include class QCheckBox; class QDoubleSpinBox; class QSlider; class QSpinBox; class QTabWidget; -class X11Backend; +class X11EvdevBackend; class XlibConfig : public ConfigPlugin, public Ui::KCMMouse { Q_OBJECT public: XlibConfig(ConfigContainer *parent, InputBackend *backend); ~XlibConfig() = default; static void kcmInit(); void load() override; void save() override; void defaults() override; private Q_SLOTS: void slotHandedChanged(int val); void slotScrollPolarityChanged(); void checkAccess(); void slotThreshChanged(int value); void slotDragStartDistChanged(int value); void slotWheelScrollLinesChanged(int value); private: double getAccel(); int getThreshold(); Handed getHandedness(); void setAccel(double); void setThreshold(int); void setHandedness(Handed); - X11Backend *m_backend; + X11EvdevBackend *m_backend; }; #endif // XLIBCONFIG_H