Commit ec352304 authored by Xuetian Weng's avatar Xuetian Weng

Add xf86-input-libinput support for X11 mouse backend.

Summary:
Added libinput support to X11 backend. It also adds an new option to backend
which is only available to libinput device currently.

Test Plan: Manually tested and inspect with xinput tool.

Reviewers: ngraham, #plasma, jriddell

Reviewed By: ngraham, #plasma, jriddell

Subscribers: subdiff, jriddell, apol, rkflx, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D8798
parent 08eec037
......@@ -112,6 +112,11 @@ add_feature_info("Evdev" EVDEV_FOUND "Evdev driver headers needed for input KCM"
find_package(Synaptics)
set_package_properties(Synaptics PROPERTIES TYPE OPTIONAL)
add_feature_info("Synaptics" SYNAPTICS_FOUND "Synaptics libraries needed for touchpad KCM")
find_package(XorgLibinput)
set_package_properties(XorgLibinput PROPERTIES TYPE OPTIONAL)
add_feature_info("XorgLibinput" XORGLIBINPUT_FOUND "Libinput driver headers needed for input KCM")
include(ConfigureChecks.cmake)
if(${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GL")
......
# - Find xorg libinput's libraries and headers.
# This module defines the following variables:
#
# XORGLIBINPUT_FOUND - true if libinput was found
# XORGLIBINPUT_INCLUDE_DIRS - include path for synaptics
# There are no libraries, just a header file
#
# Copyright (c) 2017 Weng Xuetian <wengxt@gmail.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the University nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
find_package(PkgConfig)
pkg_check_modules(PC_XORGLIBINPUT xorg-libinput)
find_path(XORGLIBINPUT_INCLUDE_DIRS
NAMES libinput-properties.h
HINTS ${PC_XORGLIBINPUT_INCLUDE_DIRS} ${PC_XORGLIBINPUT_INCLUDEDIR}
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(XorgLibinput REQUIRED_VARS XORGLIBINPUT_INCLUDE_DIRS)
mark_as_advanced(XORGLIBINPUT_INCLUDE_DIRS)
......@@ -14,7 +14,7 @@ if(X11_Xkb_FOUND AND XCB_XKB_FOUND)
add_subdirectory( keyboard )
endif()
if (EVDEV_FOUND AND X11_Xinput_FOUND)
if (EVDEV_FOUND AND XORGLIBINPUT_FOUND AND X11_Xinput_FOUND)
add_subdirectory( input )
endif()
......
......@@ -22,7 +22,9 @@
#include <QFile>
#include <QX11Info>
#include <KLocalizedString>
#include <evdev-properties.h>
#include <libinput-properties.h>
#include <X11/X.h>
#include <X11/Xlib.h>
......@@ -35,6 +37,10 @@
#include <X11/extensions/XInput.h>
#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)
{
......@@ -94,9 +100,15 @@ void X11MouseBackend::initAtom()
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);
}
......@@ -112,6 +124,16 @@ bool X11MouseBackend::supportScrollPolarity()
return m_numButtons >= 5;
}
QStringList X11MouseBackend::supportedAccelerationProfiles()
{
return m_supportedAccelerationProfiles;
}
QString X11MouseBackend::accelerationProfile()
{
return m_accelerationProfile;
}
double X11MouseBackend::accelRate()
{
return m_accelRate;
......@@ -159,6 +181,62 @@ void X11MouseBackend::load()
m_handed = MouseHanded::Left;
}
}
m_supportedAccelerationProfiles.clear();
bool adaptiveAvailable = false;
bool flatAvailable = false;
bool adaptiveEnabled = false;
bool flatEnabled = false;
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<unsigned char, ScopedXDeleter> 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;
}
}
void X11MouseBackend::apply(const MouseSettings& settings, bool force)
......@@ -204,11 +282,21 @@ void X11MouseBackend::apply(const MouseSettings& settings, bool force)
// are belong to kcm touchpad.
XIForallPointerDevices(m_dpy, [this, &settings](XDeviceInfo * info) {
int deviceid = info->id;
if (info->type == m_touchpadAtom) {
return;
}
if (libinputApplyReverseScroll(deviceid, settings.reverseScrollPolarity)) {
return;
}
evdevApplyReverseScroll(deviceid, settings.reverseScrollPolarity);
});
}
XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) {
libinputApplyAccelerationProfile(info->deviceid, settings.currentAccelProfile);
});
XChangePointerControl(m_dpy,
true, true, int(qRound(settings.accelRate * 10)), 10, settings.thresholdMove);
......@@ -323,4 +411,72 @@ bool X11MouseBackend::evdevApplyReverseScroll(int deviceid, bool reverse)
return true;
}
bool X11MouseBackend::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<unsigned char, ScopedXDeleter> 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 X11MouseBackend::libinputApplyAccelerationProfile(int deviceid, QString profile)
{
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<unsigned char, ScopedXDeleter> 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);
}
......@@ -35,6 +35,8 @@ public:
void load() override;
bool supportScrollPolarity() override;
QStringList supportedAccelerationProfiles() override;
QString accelerationProfile() override;
double accelRate() override;
MouseHanded handed() override;
int threshold() override;
......@@ -46,9 +48,18 @@ public:
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;
......@@ -57,6 +68,8 @@ private:
double m_accelRate = 1.0;
int m_threshold = 0;
int m_middleButton = -1;
QStringList m_supportedAccelerationProfiles;
QString m_accelerationProfile;
};
#endif // XLIBMOUSEBACKEND_H
......@@ -202,7 +202,7 @@
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Pointer acceleration:</string>
......@@ -212,7 +212,7 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Pointer threshold:</string>
......@@ -222,7 +222,7 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Double click interval:</string>
......@@ -232,7 +232,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Drag start time:</string>
......@@ -242,7 +242,7 @@
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Drag start distance:</string>
......@@ -252,7 +252,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Mouse wheel scrolls by:</string>
......@@ -262,7 +262,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="accel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
......@@ -293,7 +293,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QSpinBox" name="thresh">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
......@@ -312,7 +312,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<widget class="QSpinBox" name="doubleClickInterval">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
......@@ -340,7 +340,7 @@
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QSpinBox" name="dragStartTime">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
......@@ -365,7 +365,7 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QSpinBox" name="dragStartDist">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
......@@ -387,7 +387,7 @@
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<widget class="QSpinBox" name="wheelScrollLines">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
......@@ -409,6 +409,26 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="accelerationProfileLabel">
<property name="text">
<string>Acceleration Profile:</string>
</property>
<property name="buddy">
<cstring>accelProfileComboBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="accelProfileComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
......
......@@ -98,6 +98,7 @@ MouseConfig::MouseConfig(QWidget *parent, const QVariantList &args)
connect(accel, SIGNAL(valueChanged(double)), this, SLOT(changed()));
connect(thresh, SIGNAL(valueChanged(int)), this, SLOT(changed()));
connect(thresh, SIGNAL(valueChanged(int)), this, SLOT(slotThreshChanged(int)));
connect(accelProfileComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changed()));
slotThreshChanged(thresh->value());
// It would be nice if the user had a test field.
......@@ -219,6 +220,22 @@ void MouseConfig::load()
}
}
auto accelerationProfiles = 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())
......@@ -279,6 +296,7 @@ void MouseConfig::save()
settings->wheelScrollLines = wheelScrollLines->value();
settings->singleClick = !doubleClick->isChecked();
settings->reverseScrollPolarity = cbScrollPolarity->isChecked();
settings->currentAccelProfile = accelProfileComboBox->itemData(accelProfileComboBox->currentIndex()).toString();
settings->apply(backend);
KConfig config("kcminputrc");
......
......@@ -40,6 +40,8 @@ public:
// Return the value from display server or compositor if applicable.
virtual bool supportScrollPolarity() = 0;
virtual QStringList supportedAccelerationProfiles() = 0;
virtual QString accelerationProfile() = 0;
virtual double accelRate() = 0;
virtual int threshold() = 0;
virtual MouseHanded handed() = 0;
......
......@@ -52,6 +52,7 @@ void MouseSettings::load(KConfig *config, MouseBackend *backend)
}
accel = backend->accelRate();
threshold = backend->threshold();
profile = backend->accelerationProfile();
}
KConfigGroup group = config->group("Mouse");
......@@ -73,6 +74,10 @@ void MouseSettings::load(KConfig *config, MouseBackend *backend)
else if (key == "LeftHanded")
handed = MouseHanded::Left;
reverseScrollPolarity = group.readEntry("ReverseScrollPolarity", false);
currentAccelProfile = group.readEntry("AccelerationProfile");
if (currentAccelProfile.isEmpty()) {
currentAccelProfile = profile;
}
handedNeedsApply = false;
// SC/DC/AutoSelect/ChangeCursor
......@@ -119,6 +124,7 @@ void MouseSettings::save(KConfig *config)
kcminputGroup.writeEntry("MouseButtonMapping",QString("LeftHanded"));
}
kcminputGroup.writeEntry("ReverseScrollPolarity", reverseScrollPolarity);
kcminputGroup.writeEntry("AccelerationProfile", currentAccelProfile);
kcminputGroup.sync();
KSharedConfig::Ptr profile = KSharedConfig::openConfig("kdeglobals");
......
......@@ -46,6 +46,7 @@ struct MouseSettings
bool singleClick;
int wheelScrollLines;
bool reverseScrollPolarity;
QString currentAccelProfile;
};
#endif // MOUSESETTINGS_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment