Changeset View
Changeset View
Standalone View
Standalone View
kcms/input/backends/x11/x11mousebackend.cpp
Show All 17 Lines | |||||
18 | 18 | | |||
19 | #include "x11mousebackend.h" | 19 | #include "x11mousebackend.h" | ||
20 | #include "mousesettings.h" | 20 | #include "mousesettings.h" | ||
21 | #include <config-X11.h> | 21 | #include <config-X11.h> | ||
22 | 22 | | |||
23 | #include <QFile> | 23 | #include <QFile> | ||
24 | #include <QX11Info> | 24 | #include <QX11Info> | ||
25 | #include <evdev-properties.h> | 25 | #include <evdev-properties.h> | ||
26 | #include <libinput-properties.h> | ||||
26 | #include <KLocalizedString> | 27 | #include <KLocalizedString> | ||
27 | 28 | | |||
28 | #include <X11/X.h> | 29 | #include <X11/X.h> | ||
29 | #include <X11/Xlib.h> | 30 | #include <X11/Xlib.h> | ||
30 | #include <X11/Xatom.h> | 31 | #include <X11/Xatom.h> | ||
31 | #include <X11/extensions/XInput2.h> | 32 | #include <X11/extensions/XInput2.h> | ||
32 | #include <X11/extensions/XI.h> | 33 | #include <X11/extensions/XI.h> | ||
33 | #include <X11/Xutil.h> | 34 | #include <X11/Xutil.h> | ||
34 | #ifdef HAVE_XCURSOR | 35 | #ifdef HAVE_XCURSOR | ||
35 | # include <X11/Xcursor/Xcursor.h> | 36 | # include <X11/Xcursor/Xcursor.h> | ||
36 | #include <X11/extensions/XInput.h> | 37 | #include <X11/extensions/XInput.h> | ||
37 | #endif | 38 | #endif | ||
38 | 39 | | |||
40 | static const char PROFILE_NONE[] = I18N_NOOP("None"); | ||||
41 | static const char PROFILE_ADAPTIVE[] = I18N_NOOP("Adaptive"); | ||||
42 | static const char PROFILE_FLAT[] = I18N_NOOP("Flat"); | ||||
43 | | ||||
39 | struct ScopedXDeleter { | 44 | struct ScopedXDeleter { | ||
40 | static inline void cleanup(void* pointer) | 45 | static inline void cleanup(void* pointer) | ||
41 | { | 46 | { | ||
42 | if (pointer) { | 47 | if (pointer) { | ||
43 | XFree(pointer); | 48 | XFree(pointer); | ||
44 | } | 49 | } | ||
45 | } | 50 | } | ||
46 | }; | 51 | }; | ||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Line(s) | |||||
90 | } | 95 | } | ||
91 | 96 | | |||
92 | void X11MouseBackend::initAtom() | 97 | void X11MouseBackend::initAtom() | ||
93 | { | 98 | { | ||
94 | if (!m_dpy) { | 99 | if (!m_dpy) { | ||
95 | return; | 100 | return; | ||
96 | } | 101 | } | ||
97 | 102 | | |||
103 | m_libinputAccelProfileAvailableAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, True); | ||||
104 | m_libinputAccelProfileEnabledAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True); | ||||
105 | m_libinputNaturalScrollAtom = XInternAtom(m_dpy, LIBINPUT_PROP_NATURAL_SCROLL, True); | ||||
106 | | ||||
98 | m_evdevScrollDistanceAtom = XInternAtom(m_dpy, EVDEV_PROP_SCROLL_DISTANCE, True); | 107 | m_evdevScrollDistanceAtom = XInternAtom(m_dpy, EVDEV_PROP_SCROLL_DISTANCE, True); | ||
99 | m_evdevWheelEmulationAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL, True); | 108 | m_evdevWheelEmulationAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL, True); | ||
100 | m_evdevWheelEmulationAxesAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL_AXES, True); | 109 | m_evdevWheelEmulationAxesAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL_AXES, True); | ||
110 | | ||||
111 | m_touchpadAtom = XInternAtom(m_dpy, XI_TOUCHPAD, True); | ||||
101 | } | 112 | } | ||
102 | 113 | | |||
103 | 114 | | |||
104 | X11MouseBackend::~X11MouseBackend() | 115 | X11MouseBackend::~X11MouseBackend() | ||
105 | { | 116 | { | ||
106 | if (!m_platformX11 && m_dpy) { | 117 | if (!m_platformX11 && m_dpy) { | ||
107 | XCloseDisplay(m_dpy); | 118 | XCloseDisplay(m_dpy); | ||
108 | } | 119 | } | ||
109 | } | 120 | } | ||
110 | 121 | | |||
111 | bool X11MouseBackend::supportScrollPolarity() | 122 | bool X11MouseBackend::supportScrollPolarity() | ||
112 | { | 123 | { | ||
113 | return m_numButtons >= 5; | 124 | return m_numButtons >= 5; | ||
114 | } | 125 | } | ||
115 | 126 | | |||
127 | QStringList X11MouseBackend::supportedAccelerationProfiles() | ||||
128 | { | ||||
129 | return m_supportedAccelerationProfiles; | ||||
130 | } | ||||
131 | | ||||
132 | QString X11MouseBackend::accelerationProfile() | ||||
133 | { | ||||
134 | return m_accelerationProfile; | ||||
135 | } | ||||
136 | | ||||
116 | double X11MouseBackend::accelRate() | 137 | double X11MouseBackend::accelRate() | ||
117 | { | 138 | { | ||
118 | return m_accelRate; | 139 | return m_accelRate; | ||
119 | } | 140 | } | ||
120 | 141 | | |||
121 | MouseHanded X11MouseBackend::handed() | 142 | MouseHanded X11MouseBackend::handed() | ||
122 | { | 143 | { | ||
123 | return m_handed; | 144 | return m_handed; | ||
Show All 31 Lines | 153 | { | |||
155 | } else if (m_numButtons >= 3) { | 176 | } else if (m_numButtons >= 3) { | ||
156 | m_middleButton = map[1]; | 177 | m_middleButton = map[1]; | ||
157 | if (map[0] == 1 && map[2] == 3) { | 178 | if (map[0] == 1 && map[2] == 3) { | ||
158 | m_handed = MouseHanded::Right; | 179 | m_handed = MouseHanded::Right; | ||
159 | } else if (map[0] == 3 && map[2] == 1) { | 180 | } else if (map[0] == 3 && map[2] == 1) { | ||
160 | m_handed = MouseHanded::Left; | 181 | m_handed = MouseHanded::Left; | ||
161 | } | 182 | } | ||
162 | } | 183 | } | ||
184 | | ||||
185 | m_supportedAccelerationProfiles.clear(); | ||||
186 | bool adaptiveAvaiable = false; | ||||
ngraham: adaptiveAvaiable -> adaptiveAvailable (find-and-replace should take care of this, since there… | |||||
187 | bool flatAvailable = false; | ||||
188 | bool adaptiveEnabled = false; | ||||
189 | bool flatEnabled = false; | ||||
190 | XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { | ||||
191 | int deviceid = info->deviceid; | ||||
192 | Status status; | ||||
193 | Atom type_return; | ||||
194 | int format_return; | ||||
195 | unsigned long num_items_return; | ||||
196 | unsigned long bytes_after_return; | ||||
197 | | ||||
198 | unsigned char *_data = nullptr; | ||||
199 | //data returned is an 2 byte boolean | ||||
200 | status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, | ||||
201 | False, XA_INTEGER, &type_return, &format_return, | ||||
202 | &num_items_return, &bytes_after_return, &_data); | ||||
203 | QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); | ||||
204 | _data = nullptr; | ||||
205 | if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { | ||||
206 | return; | ||||
207 | } | ||||
208 | adaptiveAvaiable = adaptiveAvaiable || data[0]; | ||||
209 | flatAvailable = flatAvailable || data[1]; | ||||
210 | | ||||
211 | //data returned is an 2 byte boolean | ||||
212 | status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, 0, 2, | ||||
213 | False, XA_INTEGER, &type_return, &format_return, | ||||
214 | &num_items_return, &bytes_after_return, &_data); | ||||
215 | data.reset(_data); | ||||
216 | _data = nullptr; | ||||
217 | if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { | ||||
218 | return; | ||||
219 | } | ||||
220 | adaptiveEnabled = adaptiveEnabled || data[0]; | ||||
221 | flatEnabled = flatEnabled || data[1]; | ||||
222 | }); | ||||
223 | | ||||
224 | if (adaptiveAvaiable) { | ||||
225 | m_supportedAccelerationProfiles << PROFILE_ADAPTIVE; | ||||
226 | } | ||||
227 | if (flatAvailable) { | ||||
228 | m_supportedAccelerationProfiles << PROFILE_FLAT; | ||||
229 | } | ||||
230 | if (adaptiveAvaiable || flatAvailable) { | ||||
231 | m_supportedAccelerationProfiles << PROFILE_NONE; | ||||
232 | } | ||||
233 | | ||||
234 | m_accelerationProfile = PROFILE_NONE; | ||||
235 | if (adaptiveAvaiable) { | ||||
ngraham: Is this supposed to be "if (adaptiveEnabled) {" | |||||
236 | m_accelerationProfile = PROFILE_ADAPTIVE; | ||||
237 | } else if (flatAvailable) { | ||||
ngraham: Ditto | |||||
238 | m_accelerationProfile = PROFILE_FLAT; | ||||
239 | } | ||||
163 | } | 240 | } | ||
164 | 241 | | |||
165 | void X11MouseBackend::apply(const MouseSettings& settings, bool force) | 242 | void X11MouseBackend::apply(const MouseSettings& settings, bool force) | ||
166 | { | 243 | { | ||
167 | // 256 might seems extreme, but X has already been known to return 32, | 244 | // 256 might seems extreme, but X has already been known to return 32, | ||
168 | // and we don't want to truncate things. Xlib limits the table to 256 bytes, | 245 | // and we don't want to truncate things. Xlib limits the table to 256 bytes, | ||
169 | // so it's a good upper bound.. | 246 | // so it's a good upper bound.. | ||
170 | unsigned char map[256]; | 247 | unsigned char map[256]; | ||
Show All 29 Lines | 275 | while ((retval = XSetPointerMapping(m_dpy, map, | |||
200 | /* keep trying until the pointer is free */ | 277 | /* keep trying until the pointer is free */ | ||
201 | { }; | 278 | { }; | ||
202 | } | 279 | } | ||
203 | 280 | | |||
204 | // apply reverseScrollPolarity for all non-touchpad pointer, touchpad | 281 | // apply reverseScrollPolarity for all non-touchpad pointer, touchpad | ||
205 | // are belong to kcm touchpad. | 282 | // are belong to kcm touchpad. | ||
206 | XIForallPointerDevices(m_dpy, [this, &settings](XDeviceInfo * info) { | 283 | XIForallPointerDevices(m_dpy, [this, &settings](XDeviceInfo * info) { | ||
207 | int deviceid = info->id; | 284 | int deviceid = info->id; | ||
285 | if (info->type == m_touchpadAtom) { | ||||
286 | return; | ||||
287 | } | ||||
288 | if (libinputApplyReverseScroll(deviceid, settings.reverseScrollPolarity)) { | ||||
289 | return; | ||||
290 | } | ||||
208 | evdevApplyReverseScroll(deviceid, settings.reverseScrollPolarity); | 291 | evdevApplyReverseScroll(deviceid, settings.reverseScrollPolarity); | ||
209 | }); | 292 | }); | ||
210 | 293 | | |||
211 | } | 294 | } | ||
212 | 295 | | |||
296 | XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { | ||||
297 | libinputApplyAccelerationProfile(info->deviceid, settings.currentAccelProfile); | ||||
298 | }); | ||||
299 | | ||||
213 | XChangePointerControl(m_dpy, | 300 | XChangePointerControl(m_dpy, | ||
214 | true, true, int(qRound(settings.accelRate * 10)), 10, settings.thresholdMove); | 301 | true, true, int(qRound(settings.accelRate * 10)), 10, settings.thresholdMove); | ||
215 | 302 | | |||
216 | XFlush(m_dpy); | 303 | XFlush(m_dpy); | ||
217 | } | 304 | } | ||
218 | 305 | | |||
219 | QString X11MouseBackend::currentCursorTheme() | 306 | QString X11MouseBackend::currentCursorTheme() | ||
220 | { | 307 | { | ||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Line(s) | 393 | if (status == Success && type_return == XA_INTEGER && | |||
319 | XIChangeProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, XA_INTEGER, | 406 | XIChangeProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, XA_INTEGER, | ||
320 | 8, XIPropModeReplace, data.data(), 4); | 407 | 8, XIPropModeReplace, data.data(), 4); | ||
321 | } | 408 | } | ||
322 | } | 409 | } | ||
323 | 410 | | |||
324 | return true; | 411 | return true; | ||
325 | } | 412 | } | ||
326 | 413 | | |||
414 | bool X11MouseBackend::libinputApplyReverseScroll(int deviceid, bool reverse) | ||||
415 | { | ||||
416 | // Check atom availability first. | ||||
417 | if (m_libinputNaturalScrollAtom == None) { | ||||
418 | return false; | ||||
419 | } | ||||
420 | Status status; | ||||
421 | Atom type_return; | ||||
422 | int format_return; | ||||
423 | unsigned long num_items_return; | ||||
424 | unsigned long bytes_after_return; | ||||
425 | | ||||
426 | unsigned char *_data = nullptr; | ||||
427 | //data returned is an 1 byte boolean | ||||
428 | status = XIGetProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, 0, 1, | ||||
429 | False, XA_INTEGER, &type_return, &format_return, | ||||
430 | &num_items_return, &bytes_after_return, &_data); | ||||
431 | if (status != Success) { | ||||
432 | return false; | ||||
433 | } | ||||
434 | QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); | ||||
435 | _data = nullptr; | ||||
436 | if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 1) { | ||||
437 | return false; | ||||
438 | } | ||||
439 | unsigned char natural = reverse ? 1 : 0; | ||||
440 | XIChangeProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, XA_INTEGER, | ||||
441 | 8, XIPropModeReplace, &natural, 1); | ||||
442 | return true; | ||||
443 | } | ||||
444 | | ||||
445 | void X11MouseBackend::libinputApplyAccelerationProfile(int deviceid, QString profile) | ||||
446 | { | ||||
447 | unsigned char profileData[2]; | ||||
448 | if (profile == PROFILE_NONE) { | ||||
449 | profileData[0] = profileData[1] = 0; | ||||
450 | } else if (profile == PROFILE_ADAPTIVE) { | ||||
451 | profileData[0] = 1; | ||||
452 | profileData[1] = 0; | ||||
453 | } else if (profile == PROFILE_FLAT) { | ||||
454 | profileData[0] = 0; | ||||
455 | profileData[1] = 1; | ||||
456 | } | ||||
457 | Status status; | ||||
458 | Atom type_return; | ||||
459 | int format_return; | ||||
460 | unsigned long num_items_return; | ||||
461 | unsigned long bytes_after_return; | ||||
327 | 462 | | |||
463 | unsigned char *_data = nullptr; | ||||
464 | //data returned is an 1 byte boolean | ||||
465 | status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, | ||||
466 | False, XA_INTEGER, &type_return, &format_return, | ||||
467 | &num_items_return, &bytes_after_return, &_data); | ||||
468 | if (status != Success) { | ||||
469 | return; | ||||
470 | } | ||||
471 | QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); | ||||
472 | _data = nullptr; | ||||
473 | if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { | ||||
474 | return; | ||||
475 | } | ||||
476 | // Check availability for profile. | ||||
477 | if (profileData[0] > data[0] || profileData[1] > data[1]) { | ||||
478 | return; | ||||
479 | } | ||||
480 | XIChangeProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, XA_INTEGER, | ||||
481 | 8, XIPropModeReplace, profileData, 2); | ||||
482 | } |
adaptiveAvaiable -> adaptiveAvailable (find-and-replace should take care of this, since there are many other uses of the misspelled term)