File indexing completed on 2024-11-24 05:00:47

0001 /*
0002     SPDX-FileCopyrightText: 2019 Atul Bisht <atulbisht26@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "libinputtouchpad.h"
0008 #include "logging.h"
0009 
0010 #include <QSet>
0011 
0012 #include <limits.h>
0013 #include <stddef.h>
0014 
0015 #include <libinput-properties.h>
0016 #include <xserver-properties.h>
0017 
0018 #include <X11/extensions/XInput2.h>
0019 
0020 const Parameter libinputProperties[] = {
0021 
0022     /* libinput disable supports property */
0023     {"supportsDisableEvents", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_AVAILABLE, 8, 0},
0024     {"enabled", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED, 8, 0},
0025     {"enabledDefault", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED_DEFAULT, 8, 0},
0026 
0027     /* LeftHandSupport */
0028     {"leftHandedEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_LEFT_HANDED_DEFAULT, 8, 0},
0029     {"leftHanded", PT_INT, 0, 1, LIBINPUT_PROP_LEFT_HANDED, 8, 0},
0030 
0031     /* Disable on external mouse */
0032     {"supportsDisableEventsOnExternalMouse", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_AVAILABLE, 8, 1},
0033     {"disableEventsOnExternalMouse", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED, 8, 1},
0034     {"disableEventsOnExternalMouseDefault", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED_DEFAULT, 8, 1},
0035 
0036     /* Disable while typing */
0037     {"disableWhileTypingEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_DISABLE_WHILE_TYPING_DEFAULT, 8, 0},
0038     {"disableWhileTyping", PT_INT, 0, 1, LIBINPUT_PROP_DISABLE_WHILE_TYPING, 8, 0},
0039 
0040     /* Middle Emulation */
0041     {"middleEmulationEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED_DEFAULT, 8, 0},
0042     {"middleEmulation", PT_INT, 0, 1, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED, 8, 0},
0043 
0044     /* This is a boolean for all three fingers, no per-finger config */
0045     {"tapToClick", PT_INT, 0, 1, LIBINPUT_PROP_TAP, 8, 0},
0046     {"tapToClickEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DEFAULT, 8, 0},
0047 
0048     /* LMR 1/2/3-finger tapping mapping to Left/right/middle or left/middle/right */
0049     {"lrmTapButtonMapEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP_DEFAULT, 8, 0},
0050     {"lrmTapButtonMap", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP, 8, 0},
0051     {"lmrTapButtonMapEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP_DEFAULT, 8, 1},
0052     {"lmrTapButtonMap", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP, 8, 1},
0053 
0054     /* Tap and Drag Enabled */
0055     {"tapAndDragEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_DEFAULT, 8, 0},
0056     {"tapAndDrag", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG, 8, 0},
0057 
0058     /* Tap and Drag Lock Enabled */
0059     {"tapDragLockEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_LOCK_DEFAULT, 8, 0},
0060     {"tapDragLock", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_LOCK, 8, 0},
0061 
0062     /* libinput normalizes the accel to -1/1 */
0063     {"defaultPointerAcceleration", PT_DOUBLE, -1.0, 1.0, LIBINPUT_PROP_ACCEL_DEFAULT, 0 /*float */, 0},
0064     {"pointerAcceleration", PT_DOUBLE, -1.0, 1.0, LIBINPUT_PROP_ACCEL, 0 /*float */, 0},
0065 
0066     /* Libinput Accel Profile */
0067     {"supportsPointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, 8, 0},
0068     {"defaultPointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED_DEFAULT, 8, 0},
0069     {"pointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, 8, 0},
0070     {"supportsPointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, 8, 1},
0071     {"defaultPointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED_DEFAULT, 8, 1},
0072     {"pointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, 8, 1},
0073 
0074     /* Natural Scrolling */
0075     {"naturalScrollEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_NATURAL_SCROLL_DEFAULT, 8, 0},
0076     {"naturalScroll", PT_INT, 0, 1, LIBINPUT_PROP_NATURAL_SCROLL, 8, 0},
0077 
0078     /* Horizontal scrolling */
0079     {"horizontalScrolling", PT_INT, 0, 1, LIBINPUT_PROP_HORIZ_SCROLL_ENABLED, 8, 0},
0080 
0081     /* Two-Finger Scrolling */
0082     {"supportsScrollTwoFinger", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 0},
0083     {"scrollTwoFingerEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 0},
0084     {"scrollTwoFinger", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 0},
0085 
0086     /* Edge Scrolling */
0087     {"supportsScrollEdge", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 1},
0088     {"scrollEdgeEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 1},
0089     {"scrollEdge", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 1},
0090 
0091     /* scroll on button */
0092     {"supportsScrollOnButtonDown", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 2},
0093     {"scrollOnButtonDownEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 2},
0094     {"scrollOnButtonDown", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 2},
0095 
0096     /* Scroll Button for scroll on button Down */
0097     {"defaultScrollButton", PT_INT, 0, INT_MAX, LIBINPUT_PROP_SCROLL_BUTTON_DEFAULT, 32, 0},
0098     {"scrollButton", PT_INT, 0, INT_MAX, LIBINPUT_PROP_SCROLL_BUTTON, 32, 0},
0099 
0100     /* Click Methods */
0101     {"supportsClickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHODS_AVAILABLE, 8, 0},
0102     {"defaultClickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED_DEFAULT, 8, 0},
0103     {"clickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED, 8, 0},
0104 
0105     {"supportsClickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHODS_AVAILABLE, 8, 1},
0106     {"defaultClickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED_DEFAULT, 8, 1},
0107     {"clickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED, 8, 1},
0108 
0109     /* libinput doesn't have a separate toggle for horiz scrolling */
0110     {nullptr, PT_INT, 0, 0, nullptr, 0, 0}};
0111 
0112 Qt::MouseButtons maskBtns(Display *display, XIButtonClassInfo *buttonInfo)
0113 {
0114     Qt::MouseButtons buttons = Qt::NoButton;
0115     for (int i = 0; i < buttonInfo->num_buttons; ++i) {
0116         QByteArray reply = XGetAtomName(display, buttonInfo->labels[i]);
0117 
0118         if (reply == BTN_LABEL_PROP_BTN_LEFT) {
0119             buttons |= Qt::LeftButton;
0120         }
0121         if (reply == BTN_LABEL_PROP_BTN_RIGHT) {
0122             buttons |= Qt::RightButton;
0123         }
0124         if (reply == BTN_LABEL_PROP_BTN_MIDDLE) {
0125             buttons |= Qt::MiddleButton;
0126         }
0127         if (reply == BTN_LABEL_PROP_BTN_SIDE) {
0128             buttons |= Qt::ExtraButton1;
0129         }
0130         if (reply == BTN_LABEL_PROP_BTN_EXTRA) {
0131             buttons |= Qt::ExtraButton2;
0132         }
0133         if (reply == BTN_LABEL_PROP_BTN_FORWARD) {
0134             buttons |= Qt::ForwardButton;
0135         }
0136         if (reply == BTN_LABEL_PROP_BTN_BACK) {
0137             buttons |= Qt::BackButton;
0138         }
0139         if (reply == BTN_LABEL_PROP_BTN_TASK) {
0140             buttons |= Qt::TaskButton;
0141         }
0142     }
0143     return buttons;
0144 }
0145 
0146 LibinputTouchpad::LibinputTouchpad(Display *display, int deviceId)
0147     : LibinputCommon()
0148     , XlibTouchpad(display, deviceId)
0149 {
0150     loadSupportedProperties(libinputProperties);
0151 
0152     int nDevices = 0;
0153     XIDeviceInfo *deviceInfo = XIQueryDevice(m_display, m_deviceId, &nDevices);
0154     m_name = deviceInfo->name;
0155 
0156     for (int i = 0; i < deviceInfo->num_classes; ++i) {
0157         XIAnyClassInfo *classInfo = deviceInfo->classes[i];
0158 
0159         if (classInfo->type == XIButtonClass) {
0160             XIButtonClassInfo *btnInfo = (XIButtonClassInfo *)classInfo;
0161             m_supportedButtons.avail = true;
0162             m_supportedButtons.set(maskBtns(m_display, btnInfo));
0163         }
0164         if (classInfo->type == XITouchClass) {
0165             XITouchClassInfo *touchInfo = (XITouchClassInfo *)classInfo;
0166             m_tapFingerCount.avail = true;
0167             m_tapFingerCount.set(touchInfo->num_touches);
0168         }
0169     }
0170     XIFreeDeviceInfo(deviceInfo);
0171 
0172     /* FingerCount cannot be zero */
0173     if (!m_tapFingerCount.val) {
0174         m_tapFingerCount.avail = true;
0175 
0176         // when lmr or rml are enabled by default, fingercount must be at least 3
0177         if ((getParameter(findParameter(m_lmrTapButtonMapEnabledByDefault.name)).toBool()
0178              || getParameter(findParameter(m_lrmTapButtonMapEnabledByDefault.name)).toBool())) {
0179             m_tapFingerCount.set(3);
0180         } else {
0181             m_tapFingerCount.set(1);
0182         }
0183     }
0184     m_config = KSharedConfig::openConfig(QStringLiteral("touchpadxlibinputrc"));
0185 }
0186 
0187 bool LibinputTouchpad::getConfig()
0188 {
0189     bool success = true;
0190 
0191     success &= valueLoader(m_supportsDisableEvents);
0192     success &= valueLoader(m_enabled);
0193     success &= valueLoader(m_enabledDefault);
0194 
0195     success &= valueLoader(m_tapToClickEnabledByDefault);
0196     success &= valueLoader(m_tapToClick);
0197     success &= valueLoader(m_lrmTapButtonMapEnabledByDefault);
0198     success &= valueLoader(m_lrmTapButtonMap);
0199     success &= valueLoader(m_lmrTapButtonMapEnabledByDefault);
0200     success &= valueLoader(m_lmrTapButtonMap);
0201     success &= valueLoader(m_tapAndDragEnabledByDefault);
0202     success &= valueLoader(m_tapAndDrag);
0203     success &= valueLoader(m_tapDragLockEnabledByDefault);
0204     success &= valueLoader(m_tapDragLock);
0205 
0206     success &= valueLoader(m_leftHandedEnabledByDefault);
0207     success &= valueLoader(m_leftHanded);
0208 
0209     success &= valueLoader(m_supportsDisableEventsOnExternalMouse);
0210     success &= valueLoader(m_disableEventsOnExternalMouse);
0211     success &= valueLoader(m_disableEventsOnExternalMouseDefault);
0212 
0213     success &= valueLoader(m_disableWhileTypingEnabledByDefault);
0214     success &= valueLoader(m_disableWhileTyping);
0215 
0216     success &= valueLoader(m_middleEmulationEnabledByDefault);
0217     success &= valueLoader(m_middleEmulation);
0218 
0219     success &= valueLoader(m_defaultPointerAcceleration);
0220     success &= valueLoader(m_pointerAcceleration);
0221 
0222     success &= valueLoader(m_supportsPointerAccelerationProfileFlat);
0223     success &= valueLoader(m_defaultPointerAccelerationProfileFlat);
0224     success &= valueLoader(m_pointerAccelerationProfileFlat);
0225     success &= valueLoader(m_supportsPointerAccelerationProfileAdaptive);
0226     success &= valueLoader(m_defaultPointerAccelerationProfileAdaptive);
0227     success &= valueLoader(m_pointerAccelerationProfileAdaptive);
0228 
0229     success &= valueLoader(m_naturalScrollEnabledByDefault);
0230     success &= valueLoader(m_naturalScroll);
0231 
0232     success &= valueLoader(m_horizontalScrolling);
0233 
0234     success &= valueLoader(m_supportsScrollTwoFinger);
0235     success &= valueLoader(m_scrollTwoFingerEnabledByDefault);
0236     success &= valueLoader(m_isScrollTwoFinger);
0237 
0238     success &= valueLoader(m_supportsScrollEdge);
0239     success &= valueLoader(m_scrollEdgeEnabledByDefault);
0240     success &= valueLoader(m_isScrollEdge);
0241 
0242     success &= valueLoader(m_supportsScrollOnButtonDown);
0243     success &= valueLoader(m_scrollOnButtonDownEnabledByDefault);
0244     success &= valueLoader(m_isScrollOnButtonDown);
0245 
0246     success &= valueLoader(m_defaultScrollButton);
0247     success &= valueLoader(m_scrollButton);
0248 
0249     // click methods
0250     success &= valueLoader(m_supportsClickMethodAreas);
0251     success &= valueLoader(m_supportsClickMethodClickfinger);
0252     success &= valueLoader(m_defaultClickMethodAreas);
0253     success &= valueLoader(m_defaultClickMethodClickfinger);
0254     success &= valueLoader(m_clickMethodAreas);
0255     success &= valueLoader(m_clickMethodClickfinger);
0256 
0257     return success;
0258 }
0259 
0260 bool LibinputTouchpad::applyConfig()
0261 {
0262     QList<QString> msgs;
0263 
0264     msgs << valueWriter(m_enabled) << valueWriter(m_tapToClick) << valueWriter(m_lrmTapButtonMap) << valueWriter(m_lmrTapButtonMap) << valueWriter(m_tapAndDrag)
0265          << valueWriter(m_tapDragLock) << valueWriter(m_leftHanded) << valueWriter(m_disableWhileTyping) << valueWriter(m_middleEmulation)
0266          << valueWriter(m_pointerAcceleration) << valueWriter(m_pointerAccelerationProfileFlat) << valueWriter(m_pointerAccelerationProfileAdaptive)
0267          << valueWriter(m_naturalScroll) << valueWriter(m_horizontalScrolling) << valueWriter(m_isScrollTwoFinger) << valueWriter(m_isScrollEdge)
0268          << valueWriter(m_isScrollOnButtonDown) << valueWriter(m_scrollButton) << valueWriter(m_clickMethodAreas) << valueWriter(m_clickMethodClickfinger);
0269 
0270     bool success = true;
0271     QString error_msg;
0272 
0273     for (const QString &m : std::as_const(msgs)) {
0274         if (!m.isNull()) {
0275             qCCritical(KCM_TOUCHPAD) << "in error:" << m;
0276             if (!success) {
0277                 error_msg.append("\n");
0278             }
0279             error_msg.append(m);
0280             success = false;
0281         }
0282     }
0283 
0284     if (!success) {
0285         qCCritical(KCM_TOUCHPAD) << error_msg;
0286     }
0287 
0288     flush();
0289     return success;
0290 }
0291 
0292 bool LibinputTouchpad::getDefaultConfig()
0293 {
0294     m_enabled.set(m_enabledDefault);
0295     m_tapToClick.set(m_tapToClickEnabledByDefault);
0296     m_lrmTapButtonMap.set(m_lrmTapButtonMap);
0297     m_lmrTapButtonMap.set(m_lmrTapButtonMapEnabledByDefault);
0298     m_tapAndDrag.set(m_tapAndDragEnabledByDefault);
0299     m_tapDragLock.set(m_tapDragLockEnabledByDefault);
0300     m_leftHanded.set(m_leftHandedEnabledByDefault);
0301     m_disableEventsOnExternalMouse.set(m_disableEventsOnExternalMouseDefault);
0302     m_disableWhileTyping.set(m_disableWhileTypingEnabledByDefault);
0303     m_middleEmulation.set(m_middleEmulationEnabledByDefault);
0304     m_pointerAcceleration.set(m_defaultPointerAcceleration);
0305     m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat);
0306     m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive);
0307     m_naturalScroll.set(m_naturalScrollEnabledByDefault);
0308     m_horizontalScrolling.set(true);
0309     m_isScrollTwoFinger.set(m_scrollTwoFingerEnabledByDefault);
0310     m_isScrollEdge.set(m_scrollEdgeEnabledByDefault);
0311     m_isScrollOnButtonDown.set(m_scrollOnButtonDownEnabledByDefault);
0312     m_scrollButton.set(m_defaultScrollButton);
0313     m_clickMethodAreas.set(m_defaultClickMethodAreas);
0314     m_clickMethodClickfinger.set(m_defaultClickMethodClickfinger);
0315 
0316     return true;
0317 }
0318 
0319 bool LibinputTouchpad::isChangedConfig()
0320 {
0321     // clang-format off
0322     bool changed = m_enabled.changed() ||
0323             m_tapToClick.changed() ||
0324             m_lrmTapButtonMap.changed() ||
0325             m_lmrTapButtonMap.changed() ||
0326             m_tapAndDrag.changed() ||
0327             m_tapDragLock.changed() ||
0328             m_leftHanded.changed() ||
0329             m_disableEventsOnExternalMouse.changed() ||
0330             m_disableWhileTyping.changed() ||
0331             m_middleEmulation.changed() ||
0332             m_pointerAcceleration.changed() ||
0333             m_pointerAccelerationProfileFlat.changed() ||
0334             m_pointerAccelerationProfileAdaptive.changed() ||
0335             m_naturalScroll.changed() ||
0336             m_horizontalScrolling.changed() ||
0337             m_isScrollTwoFinger.changed() ||
0338             m_isScrollEdge.changed() ||
0339             m_isScrollOnButtonDown.changed() ||
0340             m_scrollButton.changed() ||
0341             m_clickMethodAreas.changed() ||
0342             m_clickMethodClickfinger.changed();
0343     // clang-format on
0344 
0345     return changed;
0346 }
0347 
0348 int LibinputTouchpad::touchpadOff()
0349 {
0350     return m_enabled.val;
0351 }
0352 
0353 XcbAtom &LibinputTouchpad::touchpadOffAtom()
0354 {
0355     return *m_atoms[QLatin1String(LIBINPUT_PROP_SENDEVENTS_ENABLED)].get();
0356 }
0357 
0358 template<typename T>
0359 bool LibinputTouchpad::valueLoader(Prop<T> &prop)
0360 {
0361     const Parameter *p = findParameter(QString::fromLatin1(prop.name));
0362 
0363     if (!p) {
0364         qCCritical(KCM_TOUCHPAD) << "Error on read of " << QString::fromLatin1(prop.name);
0365     }
0366 
0367     QVariant reply = getParameter(p);
0368     if (!reply.isValid()) {
0369         prop.avail = false;
0370         return true;
0371     }
0372     prop.avail = true;
0373 
0374     auto touchpadConfig = m_config->group(m_name);
0375 
0376     const T replyValue = valueLoaderPart<T>(reply);
0377     const T loadedValue = touchpadConfig.readEntry(QString(prop.name), replyValue);
0378     prop.old = replyValue;
0379     prop.val = loadedValue;
0380 
0381     return true;
0382 }
0383 
0384 template<typename T>
0385 QString LibinputTouchpad::valueWriter(const Prop<T> &prop)
0386 {
0387     const Parameter *p = findParameter(QString::fromLatin1(prop.name));
0388 
0389     if (!p || !prop.changed()) {
0390         return QString();
0391     }
0392 
0393     bool error = !setParameter(p, prop.val);
0394     if (error) {
0395         qCCritical(KCM_TOUCHPAD) << "Cannot set property " + QString::fromLatin1(prop.name);
0396         return QStringLiteral("Cannot set property ") + QString::fromLatin1(prop.name);
0397     }
0398     auto touchpadConfig = m_config->group(m_name);
0399     touchpadConfig.writeEntry(QString(prop.name), prop.val);
0400     touchpadConfig.config()->sync();
0401     return QString();
0402 }