Changeset View
Changeset View
Standalone View
Standalone View
kcms/touchpadx/src/backends/x11/xlibbackend.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (C) 2013 Alexander Mezin <mezin.alexander@gmail.com> | ||||
3 | * | ||||
4 | * This program is free software; you can redistribute it and/or modify | ||||
5 | * it under the terms of the GNU General Public License as published by | ||||
6 | * the Free Software Foundation; either version 2 of the License, or | ||||
7 | * (at your option) any later version. | ||||
8 | * | ||||
9 | * This program is distributed in the hope that it will be useful, | ||||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
12 | * GNU General Public License for more details. | ||||
13 | * | ||||
14 | * You should have received a copy of the GNU General Public License | ||||
15 | * along with this program; if not, write to the Free Software | ||||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
17 | */ | ||||
18 | | ||||
19 | #include <cstring> | ||||
20 | #include <cmath> | ||||
21 | | ||||
22 | #include <QtAlgorithms> | ||||
23 | #include <QScopedPointer> | ||||
24 | | ||||
25 | #include <KLocalizedString> | ||||
26 | #include <QDebug> | ||||
27 | | ||||
28 | //Includes are ordered this way because of #defines in Xorg's headers | ||||
29 | #include "xrecordkeyboardmonitor.h" // krazy:exclude=includes | ||||
30 | #include "xlibbackend.h" // krazy:exclude=includes | ||||
31 | #include "xlibnotifications.h" // krazy:exclude=includes | ||||
32 | #include "libinputtouchpad.h" | ||||
33 | #include "synapticstouchpad.h" | ||||
34 | #include "propertyinfo.h" | ||||
35 | | ||||
36 | #include <X11/Xlib-xcb.h> | ||||
37 | #include <X11/Xatom.h> | ||||
38 | #include <X11/extensions/XInput.h> | ||||
39 | #include <X11/extensions/XInput2.h> | ||||
40 | | ||||
41 | #include <synaptics-properties.h> | ||||
42 | #include <xserver-properties.h> | ||||
43 | | ||||
44 | struct DeviceListDeleter | ||||
45 | { | ||||
46 | static void cleanup(XDeviceInfo *p) | ||||
47 | { | ||||
48 | if (p) { | ||||
49 | XFreeDeviceList(p); | ||||
50 | } | ||||
51 | } | ||||
52 | }; | ||||
53 | | ||||
54 | void XlibBackend::XDisplayCleanup::cleanup(Display *p) | ||||
55 | { | ||||
56 | if (p) { | ||||
57 | XCloseDisplay(p); | ||||
58 | } | ||||
59 | } | ||||
60 | | ||||
61 | XlibBackend* XlibBackend::initialize(QObject *parent) | ||||
62 | { | ||||
63 | XlibBackend* backend = new XlibBackend(parent); | ||||
64 | if (!backend->m_display) { | ||||
65 | delete backend; | ||||
66 | return nullptr; | ||||
67 | } | ||||
68 | return backend; | ||||
69 | } | ||||
70 | | ||||
71 | XlibBackend::~XlibBackend() | ||||
72 | { | ||||
73 | } | ||||
74 | | ||||
75 | XlibBackend::XlibBackend(QObject *parent) : | ||||
76 | TouchpadBackend(parent), | ||||
77 | m_display(XOpenDisplay(0)), m_connection(0) | ||||
78 | { | ||||
79 | if (m_display) { | ||||
80 | m_connection = XGetXCBConnection(m_display.data()); | ||||
81 | } | ||||
82 | | ||||
83 | if (!m_connection) { | ||||
84 | m_errorString = i18n("Cannot connect to X server"); | ||||
85 | return; | ||||
86 | } | ||||
87 | | ||||
88 | m_mouseAtom.intern(m_connection, XI_MOUSE); | ||||
89 | m_keyboardAtom.intern(m_connection, XI_KEYBOARD); | ||||
90 | m_touchpadAtom.intern(m_connection, XI_TOUCHPAD); | ||||
91 | m_enabledAtom.intern(m_connection, XI_PROP_ENABLED); | ||||
92 | | ||||
93 | m_synapticsIdentifierAtom.intern(m_connection, SYNAPTICS_PROP_CAPABILITIES); | ||||
94 | m_libinputIdentifierAtom.intern(m_connection, "libinput Send Events Modes Available"); | ||||
95 | | ||||
96 | m_device.reset(findTouchpad()); | ||||
97 | if (!m_device) { | ||||
98 | m_errorString = i18n("No touchpad found"); | ||||
99 | } | ||||
100 | } | ||||
101 | | ||||
102 | XlibTouchpad* XlibBackend::findTouchpad() | ||||
103 | { | ||||
104 | int nDevices = 0; | ||||
105 | QScopedPointer<XDeviceInfo, DeviceListDeleter> | ||||
106 | deviceInfo(XListInputDevices(m_display.data(), &nDevices)); | ||||
107 | | ||||
108 | for (XDeviceInfo *info = deviceInfo.data(); | ||||
109 | info < deviceInfo.data() + nDevices; info++) | ||||
110 | { | ||||
111 | // Make sure device is touchpad | ||||
112 | if (info->type != m_touchpadAtom.atom()) { | ||||
113 | continue; | ||||
114 | } | ||||
115 | int nProperties = 0; | ||||
116 | QSharedPointer<Atom> properties( | ||||
117 | XIListProperties(m_display.data(), info->id, | ||||
118 | &nProperties), XDeleter); | ||||
119 | | ||||
120 | Atom *atom = properties.data(), *atomEnd = properties.data() + nProperties; | ||||
121 | for (; atom != atomEnd; atom++) { | ||||
122 | if (*atom == m_libinputIdentifierAtom.atom()) { | ||||
123 | return new LibinputTouchpad(m_display.data(), info->id); | ||||
124 | } else if (*atom == m_synapticsIdentifierAtom.atom()) { | ||||
125 | return new SynapticsTouchpad(m_display.data(), info->id); | ||||
126 | } | ||||
127 | } | ||||
128 | } | ||||
129 | | ||||
130 | return nullptr; | ||||
131 | } | ||||
132 | | ||||
133 | bool XlibBackend::applyConfig(const QVariantHash &p) | ||||
134 | { | ||||
135 | if (!m_device) { | ||||
136 | return false; | ||||
137 | } | ||||
138 | | ||||
139 | bool success = m_device->applyConfig(p); | ||||
140 | if (!success) { | ||||
141 | m_errorString = i18n("Cannot apply touchpad configuration"); | ||||
142 | } | ||||
143 | | ||||
144 | return success; | ||||
145 | } | ||||
146 | | ||||
147 | bool XlibBackend::getConfig(QVariantHash &p) | ||||
148 | { | ||||
149 | if (!m_device) { | ||||
150 | return false; | ||||
151 | } | ||||
152 | | ||||
153 | bool success = m_device->getConfig(p); | ||||
154 | if (!success) { | ||||
155 | m_errorString = i18n("Cannot read touchpad configuration"); | ||||
156 | } | ||||
157 | return success; | ||||
158 | } | ||||
159 | | ||||
160 | void XlibBackend::setTouchpadEnabled(bool enable) | ||||
161 | { | ||||
162 | if (!m_device) { | ||||
163 | return; | ||||
164 | } | ||||
165 | | ||||
166 | m_device->setEnabled(enable); | ||||
167 | | ||||
168 | // FIXME? This should not be needed, m_notifications should trigger | ||||
169 | // a propertyChanged signal when we enable/disable the touchpad, | ||||
170 | // that will emit touchpadStateChanged, but for some reason | ||||
171 | // XlibNotifications is not getting the property change events | ||||
172 | // so we just emit touchpadStateChanged from here as a workaround | ||||
173 | Q_EMIT touchpadStateChanged(); | ||||
174 | } | ||||
175 | | ||||
176 | void XlibBackend::setTouchpadOff(TouchpadBackend::TouchpadOffState state) | ||||
177 | { | ||||
178 | if (!m_device) { | ||||
179 | return; | ||||
180 | } | ||||
181 | | ||||
182 | int touchpadOff = 0; | ||||
183 | switch (state) { | ||||
184 | case TouchpadEnabled: | ||||
185 | touchpadOff = 0; | ||||
186 | break; | ||||
187 | case TouchpadFullyDisabled: | ||||
188 | touchpadOff = 1; | ||||
189 | break; | ||||
190 | case TouchpadTapAndScrollDisabled: | ||||
191 | touchpadOff = 2; | ||||
192 | break; | ||||
193 | default: | ||||
194 | qCritical() << "Unknown TouchpadOffState" << state; | ||||
195 | return; | ||||
196 | } | ||||
197 | | ||||
198 | m_device->setTouchpadOff(touchpadOff); | ||||
199 | } | ||||
200 | | ||||
201 | bool XlibBackend::isTouchpadAvailable() | ||||
202 | { | ||||
203 | return m_device; | ||||
204 | } | ||||
205 | | ||||
206 | bool XlibBackend::isTouchpadEnabled() | ||||
207 | { | ||||
208 | if (!m_device) { | ||||
209 | return false; | ||||
210 | } | ||||
211 | | ||||
212 | return m_device->enabled(); | ||||
213 | } | ||||
214 | | ||||
215 | TouchpadBackend::TouchpadOffState XlibBackend::getTouchpadOff() | ||||
216 | { | ||||
217 | if (!m_device) { | ||||
218 | return TouchpadFullyDisabled; | ||||
219 | } | ||||
220 | int value = m_device->touchpadOff(); | ||||
221 | switch (value) { | ||||
222 | case 0: | ||||
223 | return TouchpadEnabled; | ||||
224 | case 1: | ||||
225 | return TouchpadFullyDisabled; | ||||
226 | case 2: | ||||
227 | return TouchpadTapAndScrollDisabled; | ||||
228 | default: | ||||
229 | qCritical() << "Unknown TouchpadOff value" << value; | ||||
230 | return TouchpadFullyDisabled; | ||||
231 | } | ||||
232 | } | ||||
233 | | ||||
234 | void XlibBackend::touchpadDetached() | ||||
235 | { | ||||
236 | qWarning() << "Touchpad detached"; | ||||
237 | m_device.reset(); | ||||
238 | Q_EMIT touchpadReset(); | ||||
239 | } | ||||
240 | | ||||
241 | void XlibBackend::devicePlugged(int device) | ||||
242 | { | ||||
243 | if (!m_device) { | ||||
244 | m_device.reset(findTouchpad()); | ||||
245 | if (m_device) { | ||||
246 | qWarning() << "Touchpad reset"; | ||||
247 | m_notifications.reset(); | ||||
248 | watchForEvents(m_keyboard); | ||||
249 | Q_EMIT touchpadReset(); | ||||
250 | } | ||||
251 | } | ||||
252 | if (!m_device || device != m_device->deviceId()) { | ||||
253 | Q_EMIT mousesChanged(); | ||||
254 | } | ||||
255 | } | ||||
256 | | ||||
257 | void XlibBackend::propertyChanged(xcb_atom_t prop) | ||||
258 | { | ||||
259 | if ((m_device && prop == m_device->touchpadOffAtom().atom()) || | ||||
260 | prop == m_enabledAtom.atom()) | ||||
261 | { | ||||
262 | Q_EMIT touchpadStateChanged(); | ||||
263 | } | ||||
264 | } | ||||
265 | | ||||
266 | QStringList XlibBackend::listMouses(const QStringList &blacklist) | ||||
267 | { | ||||
268 | int nDevices = 0; | ||||
269 | QScopedPointer<XDeviceInfo, DeviceListDeleter> | ||||
270 | info(XListInputDevices(m_display.data(), &nDevices)); | ||||
271 | QStringList list; | ||||
272 | for (XDeviceInfo *i = info.data(); i != info.data() + nDevices; i++) { | ||||
273 | if (m_device && i->id == static_cast<XID>(m_device->deviceId())) { | ||||
274 | continue; | ||||
275 | } | ||||
276 | if (i->use != IsXExtensionPointer && i->use != IsXPointer) { | ||||
277 | continue; | ||||
278 | } | ||||
279 | //type = KEYBOARD && use = Pointer means usb receiver for both keyboard | ||||
280 | //and mouse | ||||
281 | if (i->type != m_mouseAtom.atom() && i->type != m_keyboardAtom.atom()) { | ||||
282 | continue; | ||||
283 | } | ||||
284 | QString name(i->name); | ||||
285 | if (blacklist.contains(name, Qt::CaseInsensitive)) { | ||||
286 | continue; | ||||
287 | } | ||||
288 | PropertyInfo enabled(m_display.data(), i->id, m_enabledAtom.atom(), 0); | ||||
289 | if (enabled.value(0) == false) { | ||||
290 | continue; | ||||
291 | } | ||||
292 | list.append(name); | ||||
293 | } | ||||
294 | | ||||
295 | return list; | ||||
296 | } | ||||
297 | | ||||
298 | void XlibBackend::watchForEvents(bool keyboard) | ||||
299 | { | ||||
300 | if (!m_notifications) { | ||||
301 | m_notifications.reset(new XlibNotifications(m_display.data(), m_device ? m_device->deviceId() : XIAllDevices)); | ||||
302 | connect(m_notifications.data(), SIGNAL(devicePlugged(int)), | ||||
303 | SLOT(devicePlugged(int))); | ||||
304 | connect(m_notifications.data(), SIGNAL(touchpadDetached()), | ||||
305 | SLOT(touchpadDetached())); | ||||
306 | connect(m_notifications.data(), SIGNAL(propertyChanged(xcb_atom_t)), | ||||
307 | SLOT(propertyChanged(xcb_atom_t))); | ||||
308 | } | ||||
309 | | ||||
310 | if (keyboard == !m_keyboard.isNull()) { | ||||
311 | return; | ||||
312 | } | ||||
313 | | ||||
314 | if (!keyboard) { | ||||
315 | m_keyboard.reset(); | ||||
316 | return; | ||||
317 | } | ||||
318 | | ||||
319 | m_keyboard.reset(new XRecordKeyboardMonitor(m_display.data())); | ||||
320 | connect(m_keyboard.data(), SIGNAL(keyboardActivityStarted()), | ||||
321 | SIGNAL(keyboardActivityStarted())); | ||||
322 | connect(m_keyboard.data(), SIGNAL(keyboardActivityFinished()), | ||||
323 | SIGNAL(keyboardActivityFinished())); | ||||
324 | } |