Changeset View
Changeset View
Standalone View
Standalone View
kcms/input/backends/x11/x11_backend.cpp
- This file was copied to kcms/input/backends/x11/x11_evdev_backend.cpp.
Show All 12 Lines | |||||
13 | * GNU General Public License for more details. | 13 | * GNU General Public License for more details. | ||
14 | * | 14 | * | ||
15 | * You should have received a copy of the GNU General Public License | 15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
18 | */ | 18 | */ | ||
19 | 19 | | |||
20 | #include "x11_backend.h" | 20 | #include "x11_backend.h" | ||
21 | #include "x11_evdev_backend.h" | ||||
22 | #include "x11_libinput_backend.h" | ||||
23 | | ||||
24 | #include "logging.h" | ||||
21 | 25 | | |||
22 | #include <config-X11.h> | 26 | #include <config-X11.h> | ||
23 | 27 | | |||
28 | #include <libinput-properties.h> | ||||
29 | | ||||
24 | #include <QFile> | 30 | #include <QFile> | ||
25 | #include <QX11Info> | 31 | #include <QX11Info> | ||
26 | #include <KLocalizedString> | 32 | #include <KLocalizedString> | ||
27 | #include <evdev-properties.h> | 33 | #include <KConfig> | ||
28 | #include <libinput-properties.h> | 34 | #include <KConfigGroup> | ||
35 | | ||||
36 | #include <klauncher_iface.h> | ||||
29 | 37 | | |||
30 | #include <X11/X.h> | 38 | #include <X11/X.h> | ||
31 | #include <X11/Xlib.h> | 39 | #include <X11/Xlib.h> | ||
32 | #include <X11/Xatom.h> | 40 | #include <X11/Xatom.h> | ||
33 | #include <X11/extensions/XInput2.h> | 41 | #include <X11/extensions/XInput2.h> | ||
34 | #include <X11/extensions/XI.h> | 42 | #include <X11/extensions/XI.h> | ||
35 | #include <X11/Xutil.h> | 43 | #include <X11/Xutil.h> | ||
36 | #ifdef HAVE_XCURSOR | 44 | #ifdef HAVE_XCURSOR | ||
37 | #include <X11/Xcursor/Xcursor.h> | 45 | #include <X11/Xcursor/Xcursor.h> | ||
38 | #include <X11/extensions/XInput.h> | 46 | #include <X11/extensions/XInput.h> | ||
39 | #endif | 47 | #endif | ||
40 | 48 | | |||
41 | static const char PROFILE_NONE[] = I18N_NOOP("None"); | 49 | X11Backend *X11Backend::implementation(QObject *parent) | ||
42 | static const char PROFILE_ADAPTIVE[] = I18N_NOOP("Adaptive"); | | |||
43 | static const char PROFILE_FLAT[] = I18N_NOOP("Flat"); | | |||
44 | | ||||
45 | struct ScopedXDeleter { | | |||
46 | static inline void cleanup(void* pointer) | | |||
47 | { | 50 | { | ||
48 | if (pointer) { | 51 | auto dpy = QX11Info::display(); | ||
49 | XFree(pointer); | 52 | Atom testAtom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL, True); | ||
50 | } | | |||
51 | } | | |||
52 | }; | | |||
53 | | ||||
54 | template<typename Callback> | | |||
55 | static void XI2ForallPointerDevices(Display* dpy, const Callback& callback) | | |||
56 | { | | |||
57 | int ndevices_return; | | |||
58 | XIDeviceInfo* info = XIQueryDevice(dpy, XIAllDevices, &ndevices_return); | | |||
59 | if (!info) { | | |||
60 | return; | | |||
61 | } | | |||
62 | for (int i = 0; i < ndevices_return; ++i) { | | |||
63 | if ((info + i)->use == XISlavePointer) { | | |||
64 | callback(info + i); | | |||
65 | } | | |||
66 | } | | |||
67 | XIFreeDeviceInfo(info); | | |||
68 | } | | |||
69 | 53 | | |||
70 | template<typename Callback> | 54 | //There are multiple possible drivers | ||
71 | static void XIForallPointerDevices(Display* dpy, const Callback& callback) | 55 | if (testAtom) { | ||
72 | { | 56 | qCDebug(KCM_INPUT) << "Using libinput driver on X11."; | ||
73 | int ndevices_return; | 57 | return new X11LibinputBackend(parent); | ||
74 | XDeviceInfo* info = XListInputDevices(dpy, &ndevices_return); | 58 | } | ||
75 | if (!info) { | 59 | else { | ||
76 | return; | 60 | qCDebug(KCM_INPUT) << "Using evdev driver on X11."; | ||
61 | return new X11EvdevBackend(parent); | ||||
77 | } | 62 | } | ||
78 | for (int i = 0; i < ndevices_return; ++i) { | | |||
79 | if (info[i].use == IsXPointer || info[i].use == IsXExtensionPointer) { | | |||
80 | callback(info + i); | | |||
81 | } | | |||
82 | } | | |||
83 | XFreeDeviceList(info); | | |||
84 | } | 63 | } | ||
85 | 64 | | |||
86 | X11Backend::X11Backend(QObject* parent) | 65 | X11Backend::X11Backend(QObject* parent) | ||
87 | : InputBackend(parent) | 66 | : InputBackend(parent) | ||
88 | { | 67 | { | ||
89 | m_mode = InputBackendMode::XEvdev; | | |||
90 | | ||||
91 | m_platformX11 = QX11Info::isPlatformX11(); | 68 | m_platformX11 = QX11Info::isPlatformX11(); | ||
92 | if (m_platformX11) { | 69 | if (m_platformX11) { | ||
93 | m_dpy = QX11Info::display(); | 70 | m_dpy = QX11Info::display(); | ||
94 | } else { | 71 | } else { | ||
95 | // TODO: remove this - not needed anymore with Wayland backend! | 72 | // TODO: remove this - not needed anymore with Wayland backend! | ||
96 | // let's hope we have a compatibility system like Xwayland ready | 73 | // let's hope we have a compatibility system like Xwayland ready | ||
97 | m_dpy = XOpenDisplay(nullptr); | 74 | m_dpy = XOpenDisplay(nullptr); | ||
98 | } | 75 | } | ||
99 | m_settings = new EvdevSettings(); | | |||
100 | initAtom(); | | |||
101 | } | 76 | } | ||
102 | 77 | | |||
103 | void X11Backend::initAtom() | | |||
104 | { | | |||
105 | if (!m_dpy) { | | |||
106 | return; | | |||
107 | } | | |||
108 | | ||||
109 | m_libinputAccelProfileAvailableAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, True); | | |||
110 | m_libinputAccelProfileEnabledAtom = XInternAtom(m_dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True); | | |||
111 | m_libinputNaturalScrollAtom = XInternAtom(m_dpy, LIBINPUT_PROP_NATURAL_SCROLL, True); | | |||
112 | | ||||
113 | m_evdevScrollDistanceAtom = XInternAtom(m_dpy, EVDEV_PROP_SCROLL_DISTANCE, True); | | |||
114 | m_evdevWheelEmulationAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL, True); | | |||
115 | m_evdevWheelEmulationAxesAtom = XInternAtom(m_dpy, EVDEV_PROP_WHEEL_AXES, True); | | |||
116 | | ||||
117 | m_touchpadAtom = XInternAtom(m_dpy, XI_TOUCHPAD, True); | | |||
118 | } | | |||
119 | | ||||
120 | | ||||
121 | X11Backend::~X11Backend() | 78 | X11Backend::~X11Backend() | ||
122 | { | 79 | { | ||
123 | if (!m_platformX11 && m_dpy) { | 80 | if (!m_platformX11 && m_dpy) { | ||
124 | XCloseDisplay(m_dpy); | 81 | XCloseDisplay(m_dpy); | ||
125 | } | 82 | } | ||
126 | delete m_settings; | | |||
127 | } | | |||
128 | | ||||
129 | bool X11Backend::supportScrollPolarity() | | |||
130 | { | | |||
131 | return m_numButtons >= 5; | | |||
132 | } | | |||
133 | | ||||
134 | QStringList X11Backend::supportedAccelerationProfiles() | | |||
135 | { | | |||
136 | return m_supportedAccelerationProfiles; | | |||
137 | } | | |||
138 | | ||||
139 | QString X11Backend::accelerationProfile() | | |||
140 | { | | |||
141 | return m_accelerationProfile; | | |||
142 | } | | |||
143 | | ||||
144 | double X11Backend::accelRate() | | |||
145 | { | | |||
146 | return m_accelRate; | | |||
147 | } | | |||
148 | | ||||
149 | Handed X11Backend::handed() | | |||
150 | { | | |||
151 | return m_handed; | | |||
152 | } | | |||
153 | | ||||
154 | int X11Backend::threshold() | | |||
155 | { | | |||
156 | return m_threshold; | | |||
157 | } | | |||
158 | | ||||
159 | void X11Backend::load() | | |||
160 | { | | |||
161 | if (!m_dpy) { | | |||
162 | return; | | |||
163 | } | | |||
164 | | ||||
165 | m_accelRate = 1.0; | | |||
166 | int accel_num, accel_den; | | |||
167 | XGetPointerControl(m_dpy, &accel_num, &accel_den, &m_threshold); | | |||
168 | m_accelRate = double(accel_num) / double(accel_den); | | |||
169 | | ||||
170 | // get settings from X server | | |||
171 | unsigned char map[256]; | | |||
172 | m_numButtons = XGetPointerMapping(m_dpy, map, 256); | | |||
173 | m_middleButton = -1; | | |||
174 | | ||||
175 | m_handed = Handed::NotSupported; | | |||
176 | // ## keep this in sync with KGlobalSettings::mouseSettings | | |||
177 | if (m_numButtons == 2) { | | |||
178 | if (map[0] == 1 && map[1] == 2) { | | |||
179 | m_handed = Handed::Right; | | |||
180 | } else if (map[0] == 2 && map[1] == 1) { | | |||
181 | m_handed = Handed::Left; | | |||
182 | } | | |||
183 | } else if (m_numButtons >= 3) { | | |||
184 | m_middleButton = map[1]; | | |||
185 | if (map[0] == 1 && map[2] == 3) { | | |||
186 | m_handed = Handed::Right; | | |||
187 | } else if (map[0] == 3 && map[2] == 1) { | | |||
188 | m_handed = Handed::Left; | | |||
189 | } | | |||
190 | } | | |||
191 | | ||||
192 | m_supportedAccelerationProfiles.clear(); | | |||
193 | bool adaptiveAvailable = false; | | |||
194 | bool flatAvailable = false; | | |||
195 | bool adaptiveEnabled = false; | | |||
196 | bool flatEnabled = false; | | |||
197 | if (m_libinputAccelProfileAvailableAtom != None && m_libinputAccelProfileEnabledAtom != None) { | | |||
198 | XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { | | |||
199 | int deviceid = info->deviceid; | | |||
200 | Status status; | | |||
201 | Atom type_return; | | |||
202 | int format_return; | | |||
203 | unsigned long num_items_return; | | |||
204 | unsigned long bytes_after_return; | | |||
205 | | ||||
206 | unsigned char *_data = nullptr; | | |||
207 | //data returned is an 2 byte boolean | | |||
208 | status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, | | |||
209 | False, XA_INTEGER, &type_return, &format_return, | | |||
210 | &num_items_return, &bytes_after_return, &_data); | | |||
211 | QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); | | |||
212 | _data = nullptr; | | |||
213 | if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { | | |||
214 | return; | | |||
215 | } | | |||
216 | adaptiveAvailable = adaptiveAvailable || data[0]; | | |||
217 | flatAvailable = flatAvailable || data[1]; | | |||
218 | | ||||
219 | //data returned is an 2 byte boolean | | |||
220 | status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, 0, 2, | | |||
221 | False, XA_INTEGER, &type_return, &format_return, | | |||
222 | &num_items_return, &bytes_after_return, &_data); | | |||
223 | data.reset(_data); | | |||
224 | _data = nullptr; | | |||
225 | if (status != Success || type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { | | |||
226 | return; | | |||
227 | } | | |||
228 | adaptiveEnabled = adaptiveEnabled || data[0]; | | |||
229 | flatEnabled = flatEnabled || data[1]; | | |||
230 | }); | | |||
231 | } | | |||
232 | | ||||
233 | if (adaptiveAvailable) { | | |||
234 | m_supportedAccelerationProfiles << PROFILE_ADAPTIVE; | | |||
235 | } | | |||
236 | if (flatAvailable) { | | |||
237 | m_supportedAccelerationProfiles << PROFILE_FLAT; | | |||
238 | } | | |||
239 | if (adaptiveAvailable || flatAvailable) { | | |||
240 | m_supportedAccelerationProfiles << PROFILE_NONE; | | |||
241 | } | | |||
242 | | ||||
243 | m_accelerationProfile = PROFILE_NONE; | | |||
244 | if (adaptiveEnabled) { | | |||
245 | m_accelerationProfile = PROFILE_ADAPTIVE; | | |||
246 | } else if (flatEnabled) { | | |||
247 | m_accelerationProfile = PROFILE_FLAT; | | |||
248 | } | | |||
249 | | ||||
250 | m_settings->load(this); | | |||
251 | } | | |||
252 | | ||||
253 | void X11Backend::apply(bool force) | | |||
254 | { | | |||
255 | // 256 might seems extreme, but X has already been known to return 32, | | |||
256 | // and we don't want to truncate things. Xlib limits the table to 256 bytes, | | |||
257 | // so it's a good upper bound.. | | |||
258 | unsigned char map[256]; | | |||
259 | XGetPointerMapping(m_dpy, map, 256); | | |||
260 | | ||||
261 | if (m_settings->handedEnabled && (m_settings->handedNeedsApply || force)) { | | |||
262 | if (m_numButtons == 1) { | | |||
263 | map[0] = (unsigned char) 1; | | |||
264 | } else if (m_numButtons == 2) { | | |||
265 | if (m_settings->handed == Handed::Right) { | | |||
266 | map[0] = (unsigned char) 1; | | |||
267 | map[1] = (unsigned char) 3; | | |||
268 | } else { | | |||
269 | map[0] = (unsigned char) 3; | | |||
270 | map[1] = (unsigned char) 1; | | |||
271 | } | | |||
272 | } else { // 3 buttons and more | | |||
273 | if (m_settings->handed == Handed::Right) { | | |||
274 | map[0] = (unsigned char) 1; | | |||
275 | map[1] = (unsigned char) m_middleButton; | | |||
276 | map[2] = (unsigned char) 3; | | |||
277 | } else { | | |||
278 | map[0] = (unsigned char) 3; | | |||
279 | map[1] = (unsigned char) m_middleButton; | | |||
280 | map[2] = (unsigned char) 1; | | |||
281 | } | | |||
282 | } | | |||
283 | | ||||
284 | int retval; | | |||
285 | if (m_numButtons >= 1) { | | |||
286 | while ((retval = XSetPointerMapping(m_dpy, map, | | |||
287 | m_numButtons)) == MappingBusy) | | |||
288 | /* keep trying until the pointer is free */ | | |||
289 | { }; | | |||
290 | } | | |||
291 | | ||||
292 | // apply reverseScrollPolarity for all non-touchpad pointer, touchpad | | |||
293 | // are belong to kcm touchpad. | | |||
294 | XIForallPointerDevices(m_dpy, [this](XDeviceInfo * info) { | | |||
295 | int deviceid = info->id; | | |||
296 | if (info->type == m_touchpadAtom) { | | |||
297 | return; | | |||
298 | } | | |||
299 | if (libinputApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity)) { | | |||
300 | return; | | |||
301 | } | | |||
302 | evdevApplyReverseScroll(deviceid, m_settings->reverseScrollPolarity); | | |||
303 | }); | | |||
304 | | ||||
305 | } | | |||
306 | | ||||
307 | XI2ForallPointerDevices(m_dpy, [&] (XIDeviceInfo *info) { | | |||
308 | libinputApplyAccelerationProfile(info->deviceid, m_settings->currentAccelProfile); | | |||
309 | }); | | |||
310 | | ||||
311 | XChangePointerControl(m_dpy, | | |||
312 | true, true, int(qRound(m_settings->accelRate * 10)), 10, m_settings->thresholdMove); | | |||
313 | | ||||
314 | XFlush(m_dpy); | | |||
315 | } | 83 | } | ||
316 | 84 | | |||
317 | QString X11Backend::currentCursorTheme() | 85 | QString X11Backend::currentCursorTheme() | ||
318 | { | 86 | { | ||
319 | if (!m_dpy) { | 87 | if (!m_dpy) { | ||
320 | return QString(); | 88 | return QString(); | ||
321 | } | 89 | } | ||
322 | 90 | | |||
Show All 25 Lines | 102 | #ifdef HAVE_XCURSOR | |||
348 | // Load the default cursor from the theme and apply it to the root window. | 116 | // Load the default cursor from the theme and apply it to the root window. | ||
349 | Cursor handle = XcursorLibraryLoadCursor(m_dpy, "left_ptr"); | 117 | Cursor handle = XcursorLibraryLoadCursor(m_dpy, "left_ptr"); | ||
350 | XDefineCursor(m_dpy, DefaultRootWindow(m_dpy), handle); | 118 | XDefineCursor(m_dpy, DefaultRootWindow(m_dpy), handle); | ||
351 | XFreeCursor(m_dpy, handle); // Don't leak the cursor | 119 | XFreeCursor(m_dpy, handle); // Don't leak the cursor | ||
352 | XFlush(m_dpy); | 120 | XFlush(m_dpy); | ||
353 | #endif | 121 | #endif | ||
354 | } | 122 | } | ||
355 | 123 | | |||
356 | bool X11Backend::evdevApplyReverseScroll(int deviceid, bool reverse) | 124 | void X11Backend::kcmInit() | ||
357 | { | 125 | { | ||
358 | // Check atom availability first. | 126 | KConfigGroup group = KConfig("kcminputrc", KConfig::NoGlobals).group("Mouse"); | ||
359 | if (m_evdevWheelEmulationAtom == None || m_evdevScrollDistanceAtom == None || | 127 | QString theme = group.readEntry("cursorTheme", QString()); | ||
360 | m_evdevWheelEmulationAxesAtom == None) { | 128 | QString size = group.readEntry("cursorSize", QString()); | ||
361 | return false; | | |||
362 | } | | |||
363 | Status status; | | |||
364 | Atom type_return; | | |||
365 | int format_return; | | |||
366 | unsigned long num_items_return; | | |||
367 | unsigned long bytes_after_return; | | |||
368 | | ||||
369 | unsigned char* _data = nullptr; | | |||
370 | //data returned is an 1 byte boolean | | |||
371 | status = XIGetProperty(m_dpy, deviceid, m_evdevWheelEmulationAtom, 0, 1, | | |||
372 | False, XA_INTEGER, &type_return, &format_return, | | |||
373 | &num_items_return, &bytes_after_return, &_data); | | |||
374 | QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); | | |||
375 | _data = nullptr; | | |||
376 | if (status != Success) { | | |||
377 | return false; | | |||
378 | } | | |||
379 | | ||||
380 | // pointer device without wheel emulation | | |||
381 | if (type_return != XA_INTEGER || data == NULL || *data == False) { | | |||
382 | status = XIGetProperty(m_dpy, deviceid, m_evdevScrollDistanceAtom, 0, 3, | | |||
383 | False, XA_INTEGER, &type_return, &format_return, | | |||
384 | &num_items_return, &bytes_after_return, &_data); | | |||
385 | data.reset(_data); | | |||
386 | _data = nullptr; | | |||
387 | // negate scroll distance | | |||
388 | if (status == Success && type_return == XA_INTEGER && | | |||
389 | format_return == 32 && num_items_return == 3) { | | |||
390 | int32_t* vals = (int32_t*)data.data(); | | |||
391 | for (unsigned long i = 0; i < num_items_return; ++i) { | | |||
392 | int32_t val = *(vals + i); | | |||
393 | *(vals + i) = (int32_t)(reverse ? -abs(val) : abs(val)); | | |||
394 | } | | |||
395 | XIChangeProperty(m_dpy, deviceid, m_evdevScrollDistanceAtom, XA_INTEGER, | | |||
396 | 32, XIPropModeReplace, data.data(), 3); | | |||
397 | } | | |||
398 | } else { // wheel emulation used, reverse wheel axes | | |||
399 | status = XIGetProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, 0, 4, | | |||
400 | False, XA_INTEGER, &type_return, &format_return, | | |||
401 | &num_items_return, &bytes_after_return, &_data); | | |||
402 | data.reset(_data); | | |||
403 | _data = nullptr; | | |||
404 | if (status == Success && type_return == XA_INTEGER && | | |||
405 | format_return == 8 && num_items_return == 4) { | | |||
406 | // when scroll direction is not reversed, | | |||
407 | // up button id should be smaller than down button id, | | |||
408 | // up/left are odd elements, down/right are even elements | | |||
409 | for (int i = 0; i < 2; ++i) { | | |||
410 | unsigned char odd = data[i * 2]; | | |||
411 | unsigned char even = data[i * 2 + 1]; | | |||
412 | unsigned char max_elem = std::max(odd, even); | | |||
413 | unsigned char min_elem = std::min(odd, even); | | |||
414 | data[i * 2] = reverse ? max_elem : min_elem; | | |||
415 | data[i * 2 + 1] = reverse ? min_elem : max_elem; | | |||
416 | } | | |||
417 | XIChangeProperty(m_dpy, deviceid, m_evdevWheelEmulationAxesAtom, XA_INTEGER, | | |||
418 | 8, XIPropModeReplace, data.data(), 4); | | |||
419 | } | | |||
420 | } | | |||
421 | 129 | | |||
422 | return true; | 130 | int intSize = -1; | ||
131 | if (size.isEmpty()) { | ||||
132 | bool ok; | ||||
133 | uint value = size.toUInt(&ok); | ||||
134 | if (ok) { | ||||
135 | intSize = value; | ||||
423 | } | 136 | } | ||
424 | | ||||
425 | bool X11Backend::libinputApplyReverseScroll(int deviceid, bool reverse) | | |||
426 | { | | |||
427 | // Check atom availability first. | | |||
428 | if (m_libinputNaturalScrollAtom == None) { | | |||
429 | return false; | | |||
430 | } | | |||
431 | Status status; | | |||
432 | Atom type_return; | | |||
433 | int format_return; | | |||
434 | unsigned long num_items_return; | | |||
435 | unsigned long bytes_after_return; | | |||
436 | | ||||
437 | unsigned char *_data = nullptr; | | |||
438 | //data returned is an 1 byte boolean | | |||
439 | status = XIGetProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, 0, 1, | | |||
440 | False, XA_INTEGER, &type_return, &format_return, | | |||
441 | &num_items_return, &bytes_after_return, &_data); | | |||
442 | if (status != Success) { | | |||
443 | return false; | | |||
444 | } | | |||
445 | QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); | | |||
446 | _data = nullptr; | | |||
447 | if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 1) { | | |||
448 | return false; | | |||
449 | } | | |||
450 | unsigned char natural = reverse ? 1 : 0; | | |||
451 | XIChangeProperty(m_dpy, deviceid, m_libinputNaturalScrollAtom, XA_INTEGER, | | |||
452 | 8, XIPropModeReplace, &natural, 1); | | |||
453 | return true; | | |||
454 | } | 137 | } | ||
138 | // Note: If you update this code, update kapplymousetheme as well. | ||||
455 | 139 | | |||
456 | void X11Backend::libinputApplyAccelerationProfile(int deviceid, QString profile) | 140 | // use a default value for theme only if it's not configured at all, not even in X resources | ||
457 | { | 141 | if (theme.isEmpty() && currentCursorTheme().isEmpty()) { | ||
458 | // Check atom availability first. | 142 | theme = "breeze_cursors"; | ||
459 | if (m_libinputAccelProfileAvailableAtom == None || m_libinputAccelProfileEnabledAtom == None) { | | |||
460 | return; | | |||
461 | } | 143 | } | ||
144 | applyCursorTheme(theme, intSize); | ||||
462 | 145 | | |||
463 | unsigned char profileData[2]; | 146 | // Tell klauncher to set the XCURSOR_THEME and XCURSOR_SIZE environment | ||
464 | if (profile == PROFILE_NONE) { | 147 | // variables when launching applications. | ||
465 | profileData[0] = profileData[1] = 0; | 148 | OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), | ||
466 | } else if (profile == PROFILE_ADAPTIVE) { | 149 | QStringLiteral("/KLauncher"), | ||
467 | profileData[0] = 1; | 150 | QDBusConnection::sessionBus()); | ||
468 | profileData[1] = 0; | 151 | if (!theme.isEmpty()) { | ||
469 | } else if (profile == PROFILE_FLAT) { | 152 | klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), theme); | ||
470 | profileData[0] = 0; | | |||
471 | profileData[1] = 1; | | |||
472 | } | | |||
473 | Status status; | | |||
474 | Atom type_return; | | |||
475 | int format_return; | | |||
476 | unsigned long num_items_return; | | |||
477 | unsigned long bytes_after_return; | | |||
478 | | ||||
479 | unsigned char *_data = nullptr; | | |||
480 | //data returned is an 1 byte boolean | | |||
481 | status = XIGetProperty(m_dpy, deviceid, m_libinputAccelProfileAvailableAtom, 0, 2, | | |||
482 | False, XA_INTEGER, &type_return, &format_return, | | |||
483 | &num_items_return, &bytes_after_return, &_data); | | |||
484 | if (status != Success) { | | |||
485 | return; | | |||
486 | } | | |||
487 | QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data); | | |||
488 | _data = nullptr; | | |||
489 | if (type_return != XA_INTEGER || !data || format_return != 8 || num_items_return != 2) { | | |||
490 | return; | | |||
491 | } | 153 | } | ||
492 | // Check availability for profile. | 154 | if (!size.isEmpty()) { | ||
493 | if (profileData[0] > data[0] || profileData[1] > data[1]) { | 155 | klauncher.setLaunchEnv(QStringLiteral("XCURSOR_SIZE"), size); | ||
494 | return; | | |||
495 | } | 156 | } | ||
496 | XIChangeProperty(m_dpy, deviceid, m_libinputAccelProfileEnabledAtom, XA_INTEGER, | | |||
497 | 8, XIPropModeReplace, profileData, 2); | | |||
498 | } | 157 | } |