File indexing completed on 2025-01-26 05:06:34
0001 /* 0002 SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "x11_libinput_dummydevice.h" 0007 #include "libinput_settings.h" 0008 0009 #include <libinput-properties.h> 0010 0011 #include <X11/Xatom.h> 0012 #include <X11/extensions/XInput.h> 0013 #include <X11/extensions/XInput2.h> 0014 0015 static Atom s_touchpadAtom; 0016 0017 template<typename Callback> 0018 static void XIForallPointerDevices(Display *dpy, const Callback &callback) 0019 { 0020 int ndevices_return; 0021 XDeviceInfo *info = XListInputDevices(dpy, &ndevices_return); 0022 if (!info) { 0023 return; 0024 } 0025 for (int i = 0; i < ndevices_return; ++i) { 0026 XDeviceInfo *dev = info + i; 0027 if ((dev->use == IsXPointer || dev->use == IsXExtensionPointer) && dev->type != s_touchpadAtom) { 0028 callback(dev); 0029 } 0030 } 0031 XFreeDeviceList(info); 0032 } 0033 0034 struct ScopedXDeleter { 0035 static inline void cleanup(void *pointer) 0036 { 0037 if (pointer) { 0038 XFree(pointer); 0039 } 0040 } 0041 }; 0042 0043 namespace 0044 { 0045 template<typename T> 0046 void valueWriterPart(T val, Atom valAtom, Display *dpy) 0047 { 0048 Q_UNUSED(val); 0049 Q_UNUSED(valAtom); 0050 Q_UNUSED(dpy); 0051 } 0052 0053 template<> 0054 void valueWriterPart<bool>(bool val, Atom valAtom, Display *dpy) 0055 { 0056 XIForallPointerDevices(dpy, [&](XDeviceInfo *info) { 0057 int deviceid = info->id; 0058 Status status; 0059 Atom type_return; 0060 int format_return; 0061 unsigned long num_items_return; 0062 unsigned long bytes_after_return; 0063 0064 unsigned char *_data = nullptr; 0065 // data returned is an 1 byte boolean 0066 status = XIGetProperty(dpy, deviceid, valAtom, 0, 1, False, XA_INTEGER, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data); 0067 if (status != Success) { 0068 return; 0069 } 0070 0071 QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); 0072 _data = nullptr; 0073 0074 if (type_return != XA_INTEGER || !data || format_return != 8) { 0075 return; 0076 } 0077 0078 unsigned char sendVal[3] = {0}; 0079 if (num_items_return == 1) { 0080 sendVal[0] = val; 0081 } else { 0082 // Special case for acceleration profile. 0083 const Atom accel = XInternAtom(dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True); 0084 if (num_items_return < 2 || num_items_return > 3 || valAtom != accel) { 0085 return; 0086 } 0087 sendVal[val] = 1; 0088 } 0089 0090 XIChangeProperty(dpy, deviceid, valAtom, XA_INTEGER, 8, XIPropModeReplace, sendVal, num_items_return); 0091 }); 0092 } 0093 0094 template<> 0095 void valueWriterPart<qreal>(qreal val, Atom valAtom, Display *dpy) 0096 { 0097 XIForallPointerDevices(dpy, [&](XDeviceInfo *info) { 0098 int deviceid = info->id; 0099 Status status; 0100 Atom float_type = XInternAtom(dpy, "FLOAT", False); 0101 Atom type_return; 0102 int format_return; 0103 unsigned long num_items_return; 0104 unsigned long bytes_after_return; 0105 0106 unsigned char *_data = nullptr; 0107 // data returned is an 1 byte boolean 0108 status = XIGetProperty(dpy, deviceid, valAtom, 0, 1, False, float_type, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data); 0109 if (status != Success) { 0110 return; 0111 } 0112 0113 QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); 0114 _data = nullptr; 0115 0116 if (type_return != float_type || !data || format_return != 32 || num_items_return != 1) { 0117 return; 0118 } 0119 0120 unsigned char buffer[4096]; 0121 float *sendPtr = (float *)buffer; 0122 *sendPtr = val; 0123 0124 XIChangeProperty(dpy, deviceid, valAtom, float_type, format_return, XIPropModeReplace, buffer, 1); 0125 }); 0126 } 0127 } 0128 0129 X11LibinputDummyDevice::X11LibinputDummyDevice(QObject *parent, Display *dpy) 0130 : QObject(parent) 0131 , m_settings(new LibinputSettings()) 0132 , m_dpy(dpy) 0133 { 0134 m_leftHanded.atom = XInternAtom(dpy, LIBINPUT_PROP_LEFT_HANDED, True); 0135 m_middleEmulation.atom = XInternAtom(dpy, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED, True); 0136 m_naturalScroll.atom = XInternAtom(dpy, LIBINPUT_PROP_NATURAL_SCROLL, True); 0137 m_pointerAcceleration.atom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL, True); 0138 m_pointerAccelerationProfileFlat.atom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True); 0139 0140 m_supportsDisableEvents.val = false; 0141 m_enabled.val = true; 0142 m_supportedButtons.val = Qt::LeftButton | Qt::MiddleButton | Qt::RightButton; 0143 m_supportsLeftHanded.val = true; 0144 m_supportsMiddleEmulation.val = true; 0145 m_middleEmulationEnabledByDefault.val = false; 0146 0147 m_supportsPointerAcceleration.val = true; 0148 m_defaultPointerAcceleration.val = 0; 0149 0150 m_supportsPointerAccelerationProfileAdaptive.val = true; 0151 m_supportsPointerAccelerationProfileFlat.val = true; 0152 0153 auto x11DefaultFlat = m_settings->load(QStringLiteral("X11LibInputXAccelProfileFlat"), false); 0154 m_defaultPointerAccelerationProfileFlat.val = x11DefaultFlat; 0155 m_defaultPointerAccelerationProfileAdaptive.val = !x11DefaultFlat; 0156 0157 m_supportsNaturalScroll.val = true; 0158 m_naturalScrollEnabledByDefault.val = false; 0159 0160 s_touchpadAtom = XInternAtom(m_dpy, XI_TOUCHPAD, True); 0161 } 0162 0163 X11LibinputDummyDevice::~X11LibinputDummyDevice() 0164 { 0165 delete m_settings; 0166 } 0167 0168 bool X11LibinputDummyDevice::getConfig() 0169 { 0170 auto reset = [this](Prop<bool> &prop, bool defVal) { 0171 prop.reset(m_settings->load(prop.cfgName, defVal)); 0172 }; 0173 0174 reset(m_leftHanded, false); 0175 0176 reset(m_middleEmulation, false); 0177 reset(m_naturalScroll, false); 0178 auto flatDefault = m_defaultPointerAccelerationProfileFlat.val; 0179 reset(m_pointerAccelerationProfileFlat, flatDefault); 0180 0181 m_pointerAccelerationProfileAdaptive.reset(!m_settings->load(m_pointerAccelerationProfileFlat.cfgName, flatDefault)); 0182 m_pointerAcceleration.reset(m_settings->load(m_pointerAcceleration.cfgName, 0.)); 0183 0184 return true; 0185 } 0186 0187 bool X11LibinputDummyDevice::getDefaultConfig() 0188 { 0189 m_leftHanded.set(false); 0190 0191 m_pointerAcceleration.set(m_defaultPointerAcceleration); 0192 m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat); 0193 m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive); 0194 0195 m_middleEmulation.set(m_middleEmulationEnabledByDefault); 0196 m_naturalScroll.set(m_naturalScrollEnabledByDefault); 0197 0198 return true; 0199 } 0200 0201 bool X11LibinputDummyDevice::applyConfig() 0202 { 0203 valueWriter(m_leftHanded); 0204 valueWriter(m_middleEmulation); 0205 valueWriter(m_naturalScroll); 0206 valueWriter(m_pointerAcceleration); 0207 valueWriter(m_pointerAccelerationProfileFlat); 0208 0209 return true; 0210 } 0211 0212 void X11LibinputDummyDevice::getDefaultConfigFromX() 0213 { 0214 // The user can override certain values in their X configuration. We want to 0215 // account for those in our default values, but if we just read this when 0216 // loading the KCM, we end up reading the current settings which may already 0217 // have been modified by us. So instead, read these defaults during startup 0218 // and write them to config, so we can later on read them again to know the 0219 // system-wide defaults. 0220 bool flatProfile = true; 0221 XIForallPointerDevices(m_dpy, [&](XDeviceInfo *info) { 0222 Atom property = m_pointerAccelerationProfileFlat.atom; 0223 Atom type_return; 0224 int format_return; 0225 unsigned long num_items_return; 0226 unsigned long bytes_after_return; 0227 unsigned char *_data = nullptr; 0228 0229 auto status = 0230 XIGetProperty(m_dpy, info->id, property, 0, 1, False, XA_INTEGER, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data); 0231 if (status != Success) { 0232 return; 0233 } 0234 0235 QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); 0236 _data = nullptr; 0237 0238 if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { 0239 return; 0240 } 0241 0242 if (data[0] == 1 && data[1] == 0) { 0243 flatProfile = false; 0244 } 0245 }); 0246 m_settings->save(QStringLiteral("X11LibInputXAccelProfileFlat"), flatProfile); 0247 } 0248 0249 template<typename T> 0250 bool X11LibinputDummyDevice::valueWriter(Prop<T> &prop) 0251 { 0252 // Check atom availability first. 0253 if (prop.atom == None) { 0254 return false; 0255 } 0256 0257 if (prop.val != prop.old) { 0258 m_settings->save(prop.cfgName, prop.val); 0259 } 0260 0261 valueWriterPart(prop.val, prop.atom, m_dpy); 0262 0263 prop.old = prop.val; 0264 0265 return true; 0266 } 0267 0268 bool X11LibinputDummyDevice::isChangedConfig() const 0269 { 0270 return m_leftHanded.changed() || m_pointerAcceleration.changed() || m_pointerAccelerationProfileFlat.changed() 0271 || m_pointerAccelerationProfileAdaptive.changed() || m_middleEmulation.changed() || m_naturalScroll.changed(); 0272 }