* Copyright (C) 2019 Atul Bisht <atulbisht26@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "libinputtouchpad.h"
#include "logging.h"
#include <QSet>
#include <stddef.h>
#include <limits.h>
#include <libinput-properties.h>
#include <xserver-properties.h>
#include <X11/extensions/XInput2.h>
const Parameter libinputProperties[] = {
{"supportsDisableEvents", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_AVAILABLE, 8, 0},
{"enabled", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED, 8, 0},
{"enabledDefault", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED_DEFAULT, 8, 0},
{"leftHandedEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_LEFT_HANDED_DEFAULT, 8, 0},
{"leftHanded", PT_INT, 0, 1, LIBINPUT_PROP_LEFT_HANDED, 8, 0},
{"supportsDisableEventsOnExternalMouse",PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_AVAILABLE, 8, 1},
{"disableEventsOnExternalMouse", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED, 8, 1},
{"disableEventsOnExternalMouseDefault", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED_DEFAULT, 8, 1},
{"disableWhileTypingEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_DISABLE_WHILE_TYPING_DEFAULT, 8, 0},
{"disableWhileTyping", PT_INT, 0, 1, LIBINPUT_PROP_DISABLE_WHILE_TYPING, 8, 0},
{"middleEmulationEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED_DEFAULT, 8, 0},
{"middleEmulation", PT_INT, 0, 1, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED, 8, 0},
{"tapToClick", PT_INT, 0, 1, LIBINPUT_PROP_TAP, 8, 0},
{"tapToClickEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DEFAULT, 8, 0},
{"lrmTapButtonMapEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP_DEFAULT, 8, 0},
{"lrmTapButtonMap", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP, 8, 0},
{"lmrTapButtonMapEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP_DEFAULT, 8, 1},
{"lmrTapButtonMap", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP, 8, 1},
{"tapAndDragEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_DEFAULT, 8, 0},
{"tapAndDrag", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG, 8, 0},
{"tapDragLockEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_LOCK_DEFAULT, 8, 0},
{"tapDragLock", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_LOCK, 8, 0},
{"defaultPointerAcceleration", PT_DOUBLE, -1.0, 1.0, LIBINPUT_PROP_ACCEL_DEFAULT, 0 , 0},
{"pointerAcceleration", PT_DOUBLE, -1.0, 1.0, LIBINPUT_PROP_ACCEL, 0 , 0},
{"supportsPointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, 8, 0},
{"defaultPointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED_DEFAULT, 8, 0},
{"pointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, 8, 0},
{"supportsPointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, 8, 1},
{"defaultPointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED_DEFAULT, 8, 1},
{"pointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, 8, 1},
{"naturalScrollEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_NATURAL_SCROLL_DEFAULT, 8, 0},
{"naturalScroll", PT_INT, 0, 1, LIBINPUT_PROP_NATURAL_SCROLL, 8, 0},
{"horizontalScrolling", PT_INT, 0, 1, LIBINPUT_PROP_HORIZ_SCROLL_ENABLED, 8, 0},
{"supportsScrollTwoFinger", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 0},
{"scrollTwoFingerEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 0},
{"scrollTwoFinger", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 0},
{"supportsScrollEdge", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 1},
{"scrollEdgeEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 1},
{"scrollEdge", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 1},
{"supportsScrollOnButtonDown", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 2},
{"scrollOnButtonDownEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 2},
{"scrollOnButtonDown", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 2},
{"defaultScrollButton", PT_INT, 0, INT_MAX, LIBINPUT_PROP_SCROLL_BUTTON_DEFAULT, 32, 0},
{"scrollButton", PT_INT, 0, INT_MAX, LIBINPUT_PROP_SCROLL_BUTTON, 32, 0},
{"supportsClickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHODS_AVAILABLE, 8, 0},
{"defaultClickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED_DEFAULT, 8, 0},
{"clickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED, 8, 0},
{"supportsClickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHODS_AVAILABLE, 8, 1},
{"defaultClickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED_DEFAULT, 8, 1},
{"clickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED, 8, 1},
{ nullptr, PT_INT, 0, 0, nullptr, 0, 0 }
};
Qt::MouseButtons maskBtns(Display *display, XIButtonClassInfo *buttonInfo)
{
Qt::MouseButtons buttons = Qt::NoButton;
for (int i = 0; i < buttonInfo->num_buttons; ++i) {
QByteArray reply = XGetAtomName(display, buttonInfo->labels[i]);
if (reply == BTN_LABEL_PROP_BTN_LEFT) {
buttons |= Qt::LeftButton;
}
if (reply == BTN_LABEL_PROP_BTN_RIGHT) {
buttons |= Qt::RightButton;
}
if (reply == BTN_LABEL_PROP_BTN_MIDDLE) {
buttons |= Qt::MiddleButton;
}
if (reply == BTN_LABEL_PROP_BTN_SIDE) {
buttons |= Qt::ExtraButton1;
}
if (reply == BTN_LABEL_PROP_BTN_EXTRA) {
buttons |= Qt::ExtraButton2;
}
if (reply == BTN_LABEL_PROP_BTN_FORWARD) {
buttons |= Qt::ForwardButton;
}
if (reply == BTN_LABEL_PROP_BTN_BACK) {
buttons |= Qt::BackButton;
}
if (reply == BTN_LABEL_PROP_BTN_TASK) {
buttons |= Qt::TaskButton;
}
}
return buttons;
}
LibinputTouchpad::LibinputTouchpad(Display *display, int deviceId):
LibinputCommon(),
XlibTouchpad(display, deviceId)
{
loadSupportedProperties(libinputProperties);
int nDevices = 0;
XIDeviceInfo *deviceInfo = XIQueryDevice(m_display, m_deviceId, &nDevices);
m_name = deviceInfo->name;
for (int i = 0; i < deviceInfo->num_classes; ++i) {
XIAnyClassInfo *classInfo = deviceInfo->classes[i];
if (classInfo->type == XIButtonClass) {
XIButtonClassInfo *btnInfo = (XIButtonClassInfo*) classInfo;
m_supportedButtons.avail = true;
m_supportedButtons.set(maskBtns(m_display, btnInfo));
}
if (classInfo->type == XITouchClass) {
XITouchClassInfo *touchInfo = (XITouchClassInfo*) classInfo;
m_tapFingerCount.avail = true;
m_tapFingerCount.set(touchInfo->num_touches);
}
}
XIFreeDeviceInfo(deviceInfo);
if (!m_tapFingerCount.val) {
m_tapFingerCount.avail = true;
m_tapFingerCount.set(1);
}
m_config = KSharedConfig::openConfig(QStringLiteral("touchpadxlibinputrc"));
}
bool LibinputTouchpad::getConfig()
{
bool success = true;
success &= valueLoader(m_supportsDisableEvents);
success &= valueLoader(m_enabled);
success &= valueLoader(m_enabledDefault);
success &= valueLoader(m_tapToClickEnabledByDefault);
success &= valueLoader(m_tapToClick);
success &= valueLoader(m_lrmTapButtonMapEnabledByDefault);
success &= valueLoader(m_lrmTapButtonMap);
success &= valueLoader(m_lmrTapButtonMapEnabledByDefault);
success &= valueLoader(m_lmrTapButtonMap);
success &= valueLoader(m_tapAndDragEnabledByDefault);
success &= valueLoader(m_tapAndDrag);
success &= valueLoader(m_tapDragLockEnabledByDefault);
success &= valueLoader(m_tapDragLock);
success &= valueLoader(m_leftHandedEnabledByDefault);
success &= valueLoader(m_leftHanded);
success &= valueLoader(m_supportsDisableEventsOnExternalMouse);
success &= valueLoader(m_disableEventsOnExternalMouse);
success &= valueLoader(m_disableEventsOnExternalMouseDefault);
success &= valueLoader(m_disableWhileTypingEnabledByDefault);
success &= valueLoader(m_disableWhileTyping);
success &= valueLoader(m_middleEmulationEnabledByDefault);
success &= valueLoader(m_middleEmulation);
success &= valueLoader(m_defaultPointerAcceleration);
success &= valueLoader(m_pointerAcceleration);
success &= valueLoader(m_supportsPointerAccelerationProfileFlat);
success &= valueLoader(m_defaultPointerAccelerationProfileFlat);
success &= valueLoader(m_pointerAccelerationProfileFlat);
success &= valueLoader(m_supportsPointerAccelerationProfileAdaptive);
success &= valueLoader(m_defaultPointerAccelerationProfileAdaptive);
success &= valueLoader(m_pointerAccelerationProfileAdaptive);
success &= valueLoader(m_naturalScrollEnabledByDefault);
success &= valueLoader(m_naturalScroll);
success &= valueLoader(m_horizontalScrolling);
success &= valueLoader(m_supportsScrollTwoFinger);
success &= valueLoader(m_scrollTwoFingerEnabledByDefault);
success &= valueLoader(m_isScrollTwoFinger);
success &= valueLoader(m_supportsScrollEdge);
success &= valueLoader(m_scrollEdgeEnabledByDefault);
success &= valueLoader(m_isScrollEdge);
success &= valueLoader(m_supportsScrollOnButtonDown);
success &= valueLoader(m_scrollOnButtonDownEnabledByDefault);
success &= valueLoader(m_isScrollOnButtonDown);
success &= valueLoader(m_defaultScrollButton);
success &= valueLoader(m_scrollButton);
success &= valueLoader(m_supportsClickMethodAreas);
success &= valueLoader(m_supportsClickMethodClickfinger);
success &= valueLoader(m_defaultClickMethodAreas);
success &= valueLoader(m_defaultClickMethodClickfinger);
success &= valueLoader(m_clickMethodAreas);
success &= valueLoader(m_clickMethodClickfinger);
return success;
}
bool LibinputTouchpad::applyConfig()
{
QVector<QString> msgs;
msgs << valueWriter(m_enabled)
<< valueWriter(m_tapToClick)
<< valueWriter(m_lrmTapButtonMap)
<< valueWriter(m_lmrTapButtonMap)
<< valueWriter(m_tapAndDrag)
<< valueWriter(m_tapDragLock)
<< valueWriter(m_leftHanded)
<< valueWriter(m_disableWhileTyping)
<< valueWriter(m_middleEmulation)
<< valueWriter(m_pointerAcceleration)
<< valueWriter(m_pointerAccelerationProfileFlat)
<< valueWriter(m_pointerAccelerationProfileAdaptive)
<< valueWriter(m_naturalScroll)
<< valueWriter(m_horizontalScrolling)
<< valueWriter(m_isScrollTwoFinger)
<< valueWriter(m_isScrollEdge)
<< valueWriter(m_isScrollOnButtonDown)
<< valueWriter(m_scrollButton)
<< valueWriter(m_clickMethodAreas)
<< valueWriter(m_clickMethodClickfinger);
bool success = true;
QString error_msg;
for (QString m : msgs) {
if (!m.isNull()) {
qCCritical(KCM_TOUCHPAD) << "in error:" << m;
if (!success) {
error_msg.append("\n");
}
error_msg.append(m);
success = false;
}
}
if (!success) {
qCCritical(KCM_TOUCHPAD) << error_msg;
}
flush();
return success;
}
bool LibinputTouchpad::getDefaultConfig()
{
m_enabled.set(m_enabledDefault);
m_tapToClick.set(m_tapToClickEnabledByDefault);
m_lrmTapButtonMap.set(m_lrmTapButtonMap);
m_lmrTapButtonMap.set(m_lmrTapButtonMapEnabledByDefault);
m_tapAndDrag.set(m_tapAndDragEnabledByDefault);
m_tapDragLock.set(m_tapDragLockEnabledByDefault);
m_leftHanded.set(m_leftHandedEnabledByDefault);
m_disableEventsOnExternalMouse.set(m_disableEventsOnExternalMouseDefault);
m_disableWhileTyping.set(m_disableWhileTypingEnabledByDefault);
m_middleEmulation.set(m_middleEmulationEnabledByDefault);
m_pointerAcceleration.set(m_defaultPointerAcceleration);
m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat);
m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive);
m_naturalScroll.set(m_naturalScrollEnabledByDefault);
m_horizontalScrolling.set(true);
m_isScrollTwoFinger.set(m_scrollTwoFingerEnabledByDefault);
m_isScrollEdge.set(m_scrollEdgeEnabledByDefault);
m_isScrollOnButtonDown.set(m_scrollOnButtonDownEnabledByDefault);
m_scrollButton.set(m_defaultScrollButton);
m_clickMethodAreas.set(m_defaultClickMethodAreas);
m_clickMethodClickfinger.set(m_defaultClickMethodClickfinger);
return true;
}
bool LibinputTouchpad::isChangedConfig()
{
bool changed = m_enabled.changed() ||
m_tapToClick.changed() ||
m_lrmTapButtonMap.changed() ||
m_lmrTapButtonMap.changed() ||
m_tapAndDrag.changed() ||
m_tapDragLock.changed() ||
m_leftHanded.changed() ||
m_disableEventsOnExternalMouse.changed() ||
m_disableWhileTyping.changed() ||
m_middleEmulation.changed() ||
m_pointerAcceleration.changed() ||
m_pointerAccelerationProfileFlat.changed() ||
m_pointerAccelerationProfileAdaptive.changed() ||
m_naturalScroll.changed() ||
m_horizontalScrolling.changed() ||
m_isScrollTwoFinger.changed() ||
m_isScrollEdge.changed() ||
m_isScrollOnButtonDown.changed() ||
m_scrollButton.changed() ||
m_clickMethodAreas.changed() ||
m_clickMethodClickfinger.changed();
return changed;
}
int LibinputTouchpad::touchpadOff()
{
return m_enabled.val;
}
XcbAtom &LibinputTouchpad::touchpadOffAtom()
{
return *m_atoms[QLatin1String(LIBINPUT_PROP_SENDEVENTS_ENABLED)].data();
}
template<typename T>
bool LibinputTouchpad::valueLoader(Prop<T> &prop)
{
const Parameter *p = findParameter(QString::fromAscii(prop.name));
if (!p) {
qCCritical(KCM_TOUCHPAD) << "Error on read of " << QString::fromAscii(prop.name);
}
QVariant reply = getParameter(p);
if (!reply.isValid()) {
prop.avail = false;
return true;
}
prop.avail = true;
auto touchpadConfig = m_config->group(m_name);
const T replyValue = valueLoaderPart<T>(reply);
const T loadedValue = touchpadConfig.readEntry(QString(prop.name), replyValue);
prop.old = replyValue;
prop.val = loadedValue;
return true;
}
template<typename T>
QString LibinputTouchpad::valueWriter(const Prop<T> &prop)
{
const Parameter *p = findParameter(QString::fromAscii(prop.name));
if (!p || !prop.changed()) {
return QString();
}
bool error = !setParameter( p, prop.val);
if (error) {
qCCritical(KCM_TOUCHPAD) << "Cannot set property " + QString::fromAscii(prop.name);
return QStringLiteral("Cannot set property ") + QString::fromAscii(prop.name);
}
auto touchpadConfig = m_config->group(m_name);
touchpadConfig.writeEntry(QString(prop.name), prop.val);
touchpadConfig.config()->sync();
return QString();
}