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 }