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

0001 #include <cmath>
0002 
0003 #include "xlibtouchpad.h"
0004 #include <X11/Xlib-xcb.h>
0005 #include <X11/extensions/XInput.h>
0006 #include <X11/extensions/XInput2.h>
0007 #include <xserver-properties.h>
0008 
0009 static QVariant negateVariant(const QVariant &value)
0010 {
0011     if (value.typeId() == QMetaType::Type::Double) {
0012         return QVariant(-value.toDouble());
0013     } else if (value.typeId() == QMetaType::Type::Int) {
0014         return QVariant(-value.toInt());
0015     }
0016     return value;
0017 }
0018 
0019 XlibTouchpad::XlibTouchpad(Display *display, int deviceId)
0020     : m_display(display)
0021     , m_connection(XGetXCBConnection(display))
0022     , m_deviceId(deviceId)
0023 {
0024     m_floatType.intern(m_connection, "FLOAT");
0025     m_enabledAtom.intern(m_connection, XI_PROP_ENABLED);
0026 }
0027 
0028 bool XlibTouchpad::applyConfig(const QVariantHash &p)
0029 {
0030     m_props.clear();
0031 
0032     bool error = false;
0033     for (const QString &name : std::as_const(m_supported)) {
0034         QVariantHash::ConstIterator i = p.find(name);
0035         if (i == p.end()) {
0036             continue;
0037         }
0038         const Parameter *par = findParameter(name);
0039         if (par) {
0040             QVariant value(i.value());
0041 
0042             double k = getPropertyScale(name);
0043             if (k != 1.0) {
0044                 bool ok = false;
0045                 value = QVariant(value.toDouble(&ok) * k);
0046                 if (!ok) {
0047                     error = true;
0048                     continue;
0049                 }
0050             }
0051 
0052             if (m_negate.contains(name)) {
0053                 QVariantHash::ConstIterator i = p.find(m_negate[name]);
0054                 if (i != p.end() && i.value().toBool()) {
0055                     value = negateVariant(value);
0056                 }
0057             }
0058 
0059             if (name == "CoastingSpeed") {
0060                 QVariantHash::ConstIterator coastingEnabled = p.find("Coasting");
0061                 if (coastingEnabled != p.end() && !coastingEnabled.value().toBool()) {
0062                     value = QVariant(0);
0063                 }
0064             }
0065 
0066             if (!setParameter(par, value)) {
0067                 error = true;
0068             }
0069         }
0070     }
0071 
0072     flush();
0073 
0074     return !error;
0075 }
0076 
0077 bool XlibTouchpad::getConfig(QVariantHash &p)
0078 {
0079     if (m_supported.isEmpty()) {
0080         return false;
0081     }
0082 
0083     m_props.clear();
0084 
0085     bool error = false;
0086     for (const QString &name : std::as_const(m_supported)) {
0087         const Parameter *par = findParameter(name);
0088         if (!par) {
0089             continue;
0090         }
0091 
0092         QVariant value(getParameter(par));
0093         if (!value.isValid()) {
0094             error = true;
0095             continue;
0096         }
0097 
0098         double k = getPropertyScale(name);
0099         if (k != 1.0) {
0100             bool ok = false;
0101             value = QVariant(value.toDouble(&ok) / k);
0102             if (!ok) {
0103                 error = true;
0104                 continue;
0105             }
0106         }
0107 
0108         if (m_negate.contains(name)) {
0109             bool negative = value.toDouble() < 0.0;
0110             p[m_negate[name]] = QVariant(negative);
0111             if (negative) {
0112                 value = negateVariant(value);
0113             }
0114         }
0115 
0116         if (name == "CoastingSpeed") {
0117             bool coasting = value.toDouble() != 0.0;
0118             p["Coasting"] = QVariant(coasting);
0119             if (!coasting) {
0120                 continue;
0121             }
0122         }
0123 
0124         p[name] = value;
0125     }
0126 
0127     return !error;
0128 }
0129 
0130 void XlibTouchpad::loadSupportedProperties(const Parameter *props)
0131 {
0132     m_paramList = props;
0133     for (const Parameter *param = props; param->name; param++) {
0134         QLatin1String name(param->prop_name);
0135 
0136         if (!m_atoms.contains(name)) {
0137             m_atoms.insert(name, std::make_shared<XcbAtom>(m_connection, param->prop_name));
0138         }
0139     }
0140 
0141     for (const Parameter *p = props; p->name; p++) {
0142         if (getParameter(p).isValid()) {
0143             m_supported.append(p->name);
0144         }
0145     }
0146 }
0147 
0148 QVariant XlibTouchpad::getParameter(const Parameter *par)
0149 {
0150     PropertyInfo *p = getDevProperty(QLatin1String(par->prop_name));
0151     if (!p || par->prop_offset >= p->nitems) {
0152         return QVariant();
0153     }
0154 
0155     return p->value(par->prop_offset);
0156 }
0157 
0158 void XlibTouchpad::flush()
0159 {
0160     for (const QLatin1String &name : std::as_const(m_changed)) {
0161         m_props[name].set();
0162     }
0163     m_changed.clear();
0164 
0165     XFlush(m_display);
0166 }
0167 
0168 double XlibTouchpad::getPropertyScale(const QString &name) const
0169 {
0170     Q_UNUSED(name);
0171     return 1.0;
0172 }
0173 
0174 PropertyInfo *XlibTouchpad::getDevProperty(const QLatin1String &propName)
0175 {
0176     if (m_props.contains(propName)) {
0177         return &m_props[propName];
0178     }
0179 
0180     if (!m_atoms.contains(propName) || !m_atoms[propName]) {
0181         return nullptr;
0182     }
0183 
0184     xcb_atom_t prop = m_atoms[propName]->atom();
0185     if (!prop) {
0186         return nullptr;
0187     }
0188 
0189     PropertyInfo p(m_display, m_deviceId, prop, m_floatType.atom());
0190     if (!p.b && !p.f && !p.i) {
0191         return nullptr;
0192     }
0193     return &m_props.insert(propName, p).value();
0194 }
0195 
0196 bool XlibTouchpad::setParameter(const Parameter *par, const QVariant &value)
0197 {
0198     QLatin1String propName(par->prop_name);
0199     PropertyInfo *p = getDevProperty(propName);
0200     if (!p || par->prop_offset >= p->nitems) {
0201         return false;
0202     }
0203 
0204     QVariant converted(value);
0205     QMetaType::Type convType = QMetaType::Type::Int;
0206     if (p->f) {
0207         convType = QMetaType::Type::Double;
0208     } else if (value.typeId() == QMetaType::Type::Double) {
0209         converted = QVariant(qRound(static_cast<qreal>(value.toDouble())));
0210     }
0211 
0212     if (!converted.convert(QMetaType(convType))) {
0213         return false;
0214     }
0215 
0216     if (converted == p->value(par->prop_offset)) {
0217         return true;
0218     }
0219 
0220     if (p->b) {
0221         p->b[par->prop_offset] = static_cast<char>(converted.toInt());
0222     } else if (p->i) {
0223         p->i[par->prop_offset] = converted.toInt();
0224     } else if (p->f) {
0225         p->f[par->prop_offset] = converted.toDouble();
0226     }
0227 
0228     m_changed.insert(propName);
0229     return true;
0230 }
0231 
0232 void XlibTouchpad::setEnabled(bool enable)
0233 {
0234     PropertyInfo enabled(m_display, m_deviceId, m_enabledAtom.atom(), 0);
0235     if (enabled.b && *(enabled.b) != enable) {
0236         *(enabled.b) = enable;
0237         enabled.set();
0238     }
0239 
0240     flush();
0241 }
0242 
0243 bool XlibTouchpad::enabled()
0244 {
0245     PropertyInfo enabled(m_display, m_deviceId, m_enabledAtom.atom(), 0);
0246     return enabled.value(0).toBool();
0247 }
0248 
0249 const Parameter *XlibTouchpad::findParameter(const QString &name)
0250 {
0251     for (const Parameter *par = m_paramList; par->name; par++) {
0252         if (name == par->name) {
0253             return par;
0254         }
0255     }
0256     return nullptr;
0257 }