1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Gamepad module |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include <QtCore/qglobal.h> |
38 | QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // GCC warnings don't make sense, so disable |
39 | |
40 | #include "qevdevgamepadbackend_p.h" |
41 | #include <QtCore/QSocketNotifier> |
42 | #include <QtCore/QLoggingCategory> |
43 | #include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h> |
44 | #include <QtCore/private/qcore_unix_p.h> |
45 | #include <linux/input.h> |
46 | |
47 | #include <cmath> |
48 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | Q_LOGGING_CATEGORY(lcEGB, "qt.gamepad") |
52 | |
53 | #ifndef BTN_TRIGGER_HAPPY1 |
54 | # define BTN_TRIGGER_HAPPY1 0x2c0 |
55 | #endif |
56 | #ifndef BTN_TRIGGER_HAPPY2 |
57 | # define BTN_TRIGGER_HAPPY2 0x2c1 |
58 | #endif |
59 | #ifndef BTN_TRIGGER_HAPPY3 |
60 | # define BTN_TRIGGER_HAPPY3 0x2c2 |
61 | #endif |
62 | #ifndef BTN_TRIGGER_HAPPY4 |
63 | # define BTN_TRIGGER_HAPPY4 0x2c3 |
64 | #endif |
65 | |
66 | QEvdevGamepadDevice::EvdevAxisInfo::EvdevAxisInfo() |
67 | : QGamepadBackend::AxisInfo<int>(0, 1, QGamepadManager::AxisInvalid) |
68 | { |
69 | } |
70 | |
71 | QEvdevGamepadDevice::EvdevAxisInfo::EvdevAxisInfo(int fd, quint16 abs, int min, int max, QGamepadManager::GamepadAxis gamepadAxis) |
72 | : QGamepadBackend::AxisInfo<int>(min, max, gamepadAxis) |
73 | , flat(0) |
74 | , gamepadMinButton(QGamepadManager::ButtonInvalid) |
75 | , gamepadMaxButton(QGamepadManager::ButtonInvalid) |
76 | , gamepadLastButton(QGamepadManager::ButtonInvalid) |
77 | { |
78 | setAbsInfo(fd, abs); |
79 | } |
80 | |
81 | double QEvdevGamepadDevice::EvdevAxisInfo::normalized(int value) const |
82 | { |
83 | double val = AxisInfo::normalized(value); |
84 | if (qAbs(t: val) <= flat) |
85 | val = 0; |
86 | return val; |
87 | } |
88 | |
89 | void QEvdevGamepadDevice::EvdevAxisInfo::setAbsInfo(int fd, int abs) |
90 | { |
91 | input_absinfo absInfo; |
92 | memset(s: &absInfo, c: 0, n: sizeof(input_absinfo)); |
93 | if (ioctl(fd: fd, EVIOCGABS(abs), &absInfo) >= 0) { |
94 | minValue = absInfo.minimum; |
95 | maxValue = absInfo.maximum; |
96 | if (maxValue - minValue) |
97 | flat = std::abs(x: absInfo.flat / double(maxValue - minValue)); |
98 | } |
99 | } |
100 | |
101 | void QEvdevGamepadDevice::EvdevAxisInfo::restoreSavedData(int fd, int abs, const QVariantMap &value) |
102 | { |
103 | gamepadAxis = QGamepadManager::GamepadAxis(value[QLatin1String("axis")].toInt()); |
104 | gamepadMinButton = QGamepadManager::GamepadButton(value[QLatin1String("minButton")].toInt()); |
105 | gamepadMaxButton = QGamepadManager::GamepadButton(value[QLatin1String("maxButton")].toInt()); |
106 | setAbsInfo(fd, abs); |
107 | } |
108 | |
109 | QVariantMap QEvdevGamepadDevice::EvdevAxisInfo::dataToSave() const |
110 | { |
111 | QVariantMap data; |
112 | data[QLatin1String("axis")] = gamepadAxis; |
113 | data[QLatin1String("minButton")] = gamepadMinButton; |
114 | data[QLatin1String("maxButton")] = gamepadMaxButton; |
115 | return data; |
116 | } |
117 | |
118 | QEvdevGamepadBackend::QEvdevGamepadBackend() |
119 | { |
120 | } |
121 | |
122 | bool QEvdevGamepadBackend::start() |
123 | { |
124 | qCDebug(lcEGB) << "start"; |
125 | QByteArray device = qgetenv(varName: "QT_GAMEPAD_DEVICE"); |
126 | if (device.isEmpty()) { |
127 | qCDebug(lcEGB) << "Using device discovery"; |
128 | m_discovery = QDeviceDiscovery::create(type: QDeviceDiscovery::Device_Joystick, parent: this); |
129 | if (m_discovery) { |
130 | const QStringList devices = m_discovery->scanConnectedDevices(); |
131 | for (const QString &devStr : devices) { |
132 | device = devStr.toUtf8(); |
133 | m_devices.append(t: newDevice(device)); |
134 | } |
135 | connect(sender: m_discovery, SIGNAL(deviceDetected(QString)), receiver: this, SLOT(handleAddedDevice(QString))); |
136 | connect(sender: m_discovery, SIGNAL(deviceRemoved(QString)), receiver: this, SLOT(handleRemovedDevice(QString))); |
137 | } else { |
138 | qWarning(msg: "No device specified, set QT_GAMEPAD_DEVICE"); |
139 | return false; |
140 | } |
141 | } else { |
142 | qCDebug(lcEGB) << "Using device"<< device; |
143 | m_devices.append(t: newDevice(device)); |
144 | } |
145 | |
146 | return true; |
147 | } |
148 | |
149 | QEvdevGamepadDevice *QEvdevGamepadBackend::newDevice(const QByteArray &device) |
150 | { |
151 | qCDebug(lcEGB) << "Opening device"<< device; |
152 | return new QEvdevGamepadDevice(device, this); |
153 | } |
154 | |
155 | QEvdevGamepadDevice *QEvdevGamepadBackend::device(int deviceId) |
156 | { |
157 | for (QEvdevGamepadDevice *device : qAsConst(t&: m_devices)) |
158 | if (device->deviceId() == deviceId) |
159 | return device; |
160 | return nullptr; |
161 | } |
162 | |
163 | void QEvdevGamepadBackend::resetConfiguration(int deviceId) |
164 | { |
165 | if (QEvdevGamepadDevice *dev = device(deviceId)) |
166 | return dev->resetConfiguration(); |
167 | } |
168 | |
169 | bool QEvdevGamepadBackend::isConfigurationNeeded(int deviceId) |
170 | { |
171 | if (QEvdevGamepadDevice *dev = device(deviceId)) |
172 | return dev->isConfigurationNeeded(); |
173 | return false; |
174 | } |
175 | |
176 | bool QEvdevGamepadBackend::configureButton(int deviceId, QGamepadManager::GamepadButton button) |
177 | { |
178 | if (QEvdevGamepadDevice *dev = device(deviceId)) |
179 | return dev->configureButton(button); |
180 | return false; |
181 | } |
182 | |
183 | bool QEvdevGamepadBackend::configureAxis(int deviceId, QGamepadManager::GamepadAxis axis) |
184 | { |
185 | if (QEvdevGamepadDevice *dev = device(deviceId)) |
186 | return dev->configureAxis(axis); |
187 | return false; |
188 | } |
189 | |
190 | bool QEvdevGamepadBackend::setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button) |
191 | { |
192 | if (QEvdevGamepadDevice *dev = device(deviceId)) |
193 | return dev->setCancelConfigureButton(button); |
194 | return false; |
195 | } |
196 | |
197 | void QEvdevGamepadBackend::stop() |
198 | { |
199 | qCDebug(lcEGB) << "stop"; |
200 | qDeleteAll(c: m_devices); |
201 | m_devices.clear(); |
202 | } |
203 | |
204 | void QEvdevGamepadBackend::handleAddedDevice(const QString &device) |
205 | { |
206 | // This does not imply that an actual controller is connected. |
207 | // When connecting the wireless receiver 4 devices will show up right away. |
208 | // Therefore gamepadAdded() will be emitted only later, when something is read. |
209 | qCDebug(lcEGB) << "Connected device"<< device; |
210 | m_devices.append(t: newDevice(device: device.toUtf8())); |
211 | } |
212 | |
213 | void QEvdevGamepadBackend::handleRemovedDevice(const QString &device) |
214 | { |
215 | qCDebug(lcEGB) << "Disconnected device"<< device; |
216 | QByteArray dev = device.toUtf8(); |
217 | for (int i = 0; i < m_devices.count(); ++i) { |
218 | if (m_devices[i]->deviceName() == dev) { |
219 | delete m_devices[i]; |
220 | m_devices.removeAt(i); |
221 | break; |
222 | } |
223 | } |
224 | } |
225 | |
226 | QEvdevGamepadDevice::QEvdevGamepadDevice(const QByteArray &dev, QEvdevGamepadBackend *backend) |
227 | : m_dev(dev), |
228 | m_backend(backend), |
229 | m_fd(-1), |
230 | m_productId(0), |
231 | m_needsConfigure(true), |
232 | m_notifier(0), |
233 | m_configureButton(QGamepadManager::ButtonInvalid), |
234 | m_configureAxis(QGamepadManager::AxisInvalid) |
235 | { |
236 | openDevice(dev); |
237 | } |
238 | |
239 | QEvdevGamepadDevice::~QEvdevGamepadDevice() |
240 | { |
241 | if (m_fd != -1) |
242 | QT_CLOSE(fd: m_fd); |
243 | |
244 | if (m_productId) |
245 | emit m_backend->gamepadRemoved(deviceId: m_productId); |
246 | } |
247 | |
248 | void QEvdevGamepadDevice::resetConfiguration() |
249 | { |
250 | m_axisMap.insert(ABS_X, avalue: EvdevAxisInfo(m_fd, ABS_X, -32768, 32767, QGamepadManager::AxisLeftX)); |
251 | m_axisMap.insert(ABS_Y, avalue: EvdevAxisInfo(m_fd, ABS_Y, -32768, 32767, QGamepadManager::AxisLeftY)); |
252 | m_axisMap.insert(ABS_RX, avalue: EvdevAxisInfo(m_fd, ABS_RX, -32768, 32767, QGamepadManager::AxisRightX)); |
253 | m_axisMap.insert(ABS_RY, avalue: EvdevAxisInfo(m_fd, ABS_RY, -32768, 32767, QGamepadManager::AxisRightY)); |
254 | m_axisMap.insert(ABS_Z, avalue: EvdevAxisInfo(m_fd, ABS_Z, 0, 255)); |
255 | m_axisMap[ABS_Z].gamepadMinButton = QGamepadManager::ButtonL2; |
256 | m_axisMap[ABS_Z].gamepadMaxButton = QGamepadManager::ButtonL2; |
257 | |
258 | m_axisMap.insert(ABS_RZ, avalue: EvdevAxisInfo(m_fd, ABS_RZ, 0, 255)); |
259 | m_axisMap[ABS_RZ].gamepadMinButton = QGamepadManager::ButtonR2; |
260 | m_axisMap[ABS_RZ].gamepadMaxButton = QGamepadManager::ButtonR2; |
261 | |
262 | m_axisMap.insert(ABS_HAT0X, avalue: EvdevAxisInfo(m_fd, ABS_HAT0X, -1, 1)); |
263 | m_axisMap[ABS_HAT0X].gamepadMinButton = QGamepadManager::ButtonLeft; |
264 | m_axisMap[ABS_HAT0X].gamepadMaxButton = QGamepadManager::ButtonRight; |
265 | |
266 | m_axisMap.insert(ABS_HAT0Y, avalue: EvdevAxisInfo(m_fd, ABS_HAT0Y, -1, 1)); |
267 | m_axisMap[ABS_HAT0Y].gamepadMinButton = QGamepadManager::ButtonUp; |
268 | m_axisMap[ABS_HAT0Y].gamepadMaxButton = QGamepadManager::ButtonDown; |
269 | |
270 | m_buttonsMap[BTN_START] = QGamepadManager::ButtonStart; |
271 | m_buttonsMap[BTN_SELECT] = QGamepadManager::ButtonSelect; |
272 | m_buttonsMap[BTN_MODE] = QGamepadManager::ButtonGuide; |
273 | m_buttonsMap[BTN_X] = QGamepadManager::ButtonX; |
274 | m_buttonsMap[BTN_Y] = QGamepadManager::ButtonY; |
275 | m_buttonsMap[BTN_A] = QGamepadManager::ButtonA; |
276 | m_buttonsMap[BTN_B] = QGamepadManager::ButtonB; |
277 | m_buttonsMap[BTN_TL] = QGamepadManager::ButtonL1; |
278 | m_buttonsMap[BTN_TR] = QGamepadManager::ButtonR1; |
279 | m_buttonsMap[BTN_TL2] = QGamepadManager::ButtonL2; |
280 | m_buttonsMap[BTN_TR2] = QGamepadManager::ButtonR2; |
281 | m_buttonsMap[BTN_THUMB] = m_buttonsMap[BTN_THUMBL] = QGamepadManager::ButtonL3; |
282 | m_buttonsMap[BTN_THUMBR] = QGamepadManager::ButtonR3; |
283 | m_buttonsMap[BTN_TRIGGER_HAPPY1] = QGamepadManager::ButtonLeft; |
284 | m_buttonsMap[BTN_TRIGGER_HAPPY2] = QGamepadManager::ButtonRight; |
285 | m_buttonsMap[BTN_TRIGGER_HAPPY3] = QGamepadManager::ButtonUp; |
286 | m_buttonsMap[BTN_TRIGGER_HAPPY4] = QGamepadManager::ButtonDown; |
287 | |
288 | if (m_productId) |
289 | m_backend->saveSettings(productId: m_productId, value: QVariant()); |
290 | } |
291 | |
292 | bool QEvdevGamepadDevice::isConfigurationNeeded() |
293 | { |
294 | return m_needsConfigure; |
295 | } |
296 | |
297 | bool QEvdevGamepadDevice::configureButton(QGamepadManager::GamepadButton button) |
298 | { |
299 | m_configureButton = button; |
300 | return true; |
301 | } |
302 | |
303 | bool QEvdevGamepadDevice::configureAxis(QGamepadManager::GamepadAxis axis) |
304 | { |
305 | m_configureAxis = axis; |
306 | return true; |
307 | } |
308 | |
309 | bool QEvdevGamepadDevice::setCancelConfigureButton(QGamepadManager::GamepadButton button) |
310 | { |
311 | m_configureCancelButton = button; |
312 | return true; |
313 | } |
314 | |
315 | bool QEvdevGamepadDevice::openDevice(const QByteArray &dev) |
316 | { |
317 | m_fd = QT_OPEN(pathname: dev.constData(), O_RDONLY | O_NDELAY, mode: 0); |
318 | |
319 | if (m_fd >= 0) { |
320 | m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); |
321 | connect(sender: m_notifier, SIGNAL(activated(int)), receiver: this, SLOT(readData())); |
322 | qCDebug(lcEGB) << "Successfully opened"<< dev; |
323 | } else { |
324 | qErrnoWarning(errno, msg: "Gamepad: Cannot open input device %s", qPrintable(dev)); |
325 | return false; |
326 | } |
327 | |
328 | input_id id; |
329 | if (ioctl(fd: m_fd, EVIOCGID, &id) >= 0) { |
330 | m_productId = id.product; |
331 | |
332 | QVariant settings = m_backend->readSettings(productId: m_productId); |
333 | if (!settings.isNull()) { |
334 | m_needsConfigure = false; |
335 | QVariantMap data = settings.toMap()[QLatin1String("axes")].toMap(); |
336 | for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it) { |
337 | const int key = it.key().toInt(); |
338 | m_axisMap[key].restoreSavedData(fd: m_fd, abs: key, value: it.value().toMap()); |
339 | } |
340 | |
341 | data = settings.toMap()[QLatin1String("buttons")].toMap(); |
342 | for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it) |
343 | m_buttonsMap[it.key().toInt()] = QGamepadManager::GamepadButton(it.value().toInt()); |
344 | } |
345 | |
346 | emit m_backend->gamepadAdded(deviceId: m_productId); |
347 | |
348 | // same as libevdev::libevdev_set_fd() in libevdev.c |
349 | char buffer[256]; |
350 | memset(s: buffer, c: 0, n: sizeof(buffer)); |
351 | if (ioctl(fd: m_fd, EVIOCGNAME(sizeof(buffer) - 1), buffer) >= 0) |
352 | emit m_backend->gamepadNamed(deviceId: m_productId, name: QString::fromUtf8(str: buffer)); |
353 | |
354 | } else { |
355 | QT_CLOSE(fd: m_fd); |
356 | m_fd = -1; |
357 | return false; |
358 | } |
359 | |
360 | if (m_needsConfigure) |
361 | resetConfiguration(); |
362 | |
363 | qCDebug(lcEGB) << "Axis limits:"<< m_axisMap; |
364 | |
365 | return true; |
366 | } |
367 | |
368 | QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::EvdevAxisInfo &axisInfo) |
369 | { |
370 | dbg.nospace() << "AxisInfo(min="<< axisInfo.minValue << ", max="<< axisInfo.maxValue << ")"; |
371 | return dbg.space(); |
372 | } |
373 | |
374 | void QEvdevGamepadDevice::readData() |
375 | { |
376 | input_event buffer[32]; |
377 | int events = 0, n = 0; |
378 | for (; ;) { |
379 | events = QT_READ(fd: m_fd, data: reinterpret_cast<char*>(buffer) + n, maxlen: sizeof(buffer) - n); |
380 | if (events <= 0) |
381 | goto err; |
382 | n += events; |
383 | if (n % sizeof(::input_event) == 0) |
384 | break; |
385 | } |
386 | |
387 | n /= sizeof(::input_event); |
388 | |
389 | for (int i = 0; i < n; ++i) |
390 | processInputEvent(e: &buffer[i]); |
391 | |
392 | return; |
393 | |
394 | err: |
395 | if (!events) { |
396 | qWarning(msg: "Gamepad: Got EOF from input device"); |
397 | return; |
398 | } else if (events < 0) { |
399 | if (errno != EINTR && errno != EAGAIN) { |
400 | qErrnoWarning(errno, msg: "Gamepad: Could not read from input device"); |
401 | if (errno == ENODEV) { // device got disconnected -> stop reading |
402 | delete m_notifier; |
403 | m_notifier = 0; |
404 | QT_CLOSE(fd: m_fd); |
405 | m_fd = -1; |
406 | } |
407 | } |
408 | } |
409 | } |
410 | |
411 | void QEvdevGamepadDevice::saveData() |
412 | { |
413 | if (!m_productId) |
414 | return ; |
415 | |
416 | QVariantMap settings, data; |
417 | for (AxisMap::const_iterator it = m_axisMap.begin(); it != m_axisMap.end(); ++it) |
418 | data[QString::number(it.key())] = it.value().dataToSave(); |
419 | settings[QLatin1String("axes")] = data; |
420 | |
421 | data.clear(); |
422 | for (ButtonsMap::const_iterator it = m_buttonsMap.begin(); it != m_buttonsMap.end(); ++it) |
423 | data[QString::number(it.key())] = it.value(); |
424 | |
425 | settings[QLatin1String("buttons")] = data; |
426 | |
427 | m_backend->saveSettings(productId: m_productId, value: settings); |
428 | } |
429 | |
430 | void QEvdevGamepadDevice::processInputEvent(input_event *e) |
431 | { |
432 | if (e->type == EV_KEY) { |
433 | QGamepadManager::GamepadButton btn = QGamepadManager::ButtonInvalid; |
434 | ButtonsMap::const_iterator it = m_buttonsMap.find(akey: e->code); |
435 | if (it != m_buttonsMap.end()) |
436 | btn = it.value(); |
437 | |
438 | const bool pressed = e->value; |
439 | if (m_configureCancelButton != QGamepadManager::ButtonInvalid && |
440 | m_configureCancelButton != m_configureButton && |
441 | !pressed && btn == m_configureCancelButton && |
442 | (m_configureButton != QGamepadManager::ButtonInvalid || |
443 | m_configureAxis != QGamepadManager::AxisInvalid)) { |
444 | m_configureButton = QGamepadManager::ButtonInvalid; |
445 | m_configureAxis = QGamepadManager::AxisInvalid; |
446 | emit m_backend->configurationCanceled(deviceId: m_productId); |
447 | return; |
448 | } |
449 | |
450 | if (!pressed && m_configureButton != QGamepadManager::ButtonInvalid) { |
451 | m_buttonsMap[e->code] = m_configureButton; |
452 | QGamepadManager::GamepadButton but = m_configureButton; |
453 | m_configureButton = QGamepadManager::ButtonInvalid; |
454 | saveData(); |
455 | emit m_backend->buttonConfigured(deviceId: m_productId, button: but); |
456 | } |
457 | |
458 | it = m_buttonsMap.find(akey: e->code); |
459 | if (it != m_buttonsMap.end()) |
460 | btn = it.value(); |
461 | |
462 | if (btn != QGamepadManager::ButtonInvalid) { |
463 | if (pressed) |
464 | emit m_backend->gamepadButtonPressed(deviceId: m_productId, button: btn, value: 1.0); |
465 | else |
466 | emit m_backend->gamepadButtonReleased(deviceId: m_productId, button: btn); |
467 | } |
468 | } else if (e->type == EV_ABS) { |
469 | if (m_configureAxis != QGamepadManager::AxisInvalid) { |
470 | EvdevAxisInfo inf(m_fd, e->code, -32768, 32767, m_configureAxis); |
471 | if (std::abs(x: inf.normalized(value: e->value)) == 1) { |
472 | m_axisMap.insert(akey: e->code, avalue: EvdevAxisInfo(m_fd, e->code, -32768, 32767, m_configureAxis)); |
473 | |
474 | QGamepadManager::GamepadAxis axis = m_configureAxis; |
475 | m_configureAxis = QGamepadManager::AxisInvalid; |
476 | |
477 | saveData(); |
478 | emit m_backend->axisConfigured(deviceId: m_productId, axis); |
479 | } else { |
480 | return; |
481 | } |
482 | } |
483 | |
484 | AxisMap::iterator it = m_axisMap.find(akey: e->code); |
485 | if (m_configureButton != QGamepadManager::ButtonInvalid) { |
486 | EvdevAxisInfo axisInfo; |
487 | if (it != m_axisMap.end()) |
488 | axisInfo = it.value(); |
489 | else |
490 | axisInfo = EvdevAxisInfo(m_fd, e->code); |
491 | |
492 | axisInfo.gamepadAxis = QGamepadManager::AxisInvalid; |
493 | |
494 | bool save = false; |
495 | if (e->value == axisInfo.minValue) { |
496 | axisInfo.gamepadMinButton = m_configureButton; |
497 | if (axisInfo.gamepadMaxButton != QGamepadManager::ButtonInvalid) |
498 | axisInfo.gamepadMaxButton = m_configureButton; |
499 | save = true; |
500 | } else if (e->value == axisInfo.maxValue) { |
501 | axisInfo.gamepadMaxButton = m_configureButton; |
502 | if (axisInfo.gamepadMinButton != QGamepadManager::ButtonInvalid) |
503 | axisInfo.gamepadMinButton = m_configureButton; |
504 | save = true; |
505 | } |
506 | |
507 | if (save) { |
508 | QGamepadManager::GamepadButton but = m_configureButton; |
509 | m_configureButton = QGamepadManager::ButtonInvalid; |
510 | if (but == QGamepadManager::ButtonL2 || but == QGamepadManager::ButtonR2) |
511 | m_axisMap.insert(akey: e->code, avalue: axisInfo); |
512 | saveData(); |
513 | emit m_backend->buttonConfigured(deviceId: m_productId, button: but); |
514 | } |
515 | } |
516 | |
517 | it = m_axisMap.find(akey: e->code); |
518 | if (it == m_axisMap.end()) |
519 | return; |
520 | |
521 | EvdevAxisInfo &info = it.value(); |
522 | |
523 | double val = info.normalized(value: e->value); |
524 | |
525 | if (info.gamepadAxis != QGamepadManager::AxisInvalid) |
526 | emit m_backend->gamepadAxisMoved(deviceId: m_productId, axis: info.gamepadAxis, value: val); |
527 | |
528 | if (info.gamepadMaxButton == info.gamepadMinButton && |
529 | info.gamepadMaxButton != QGamepadManager::ButtonInvalid) { |
530 | if (val) |
531 | emit m_backend->gamepadButtonPressed(deviceId: m_productId, button: info.gamepadMaxButton, value: std::abs(x: val)); |
532 | else |
533 | emit m_backend->gamepadButtonReleased(deviceId: m_productId, button: info.gamepadMaxButton); |
534 | } else { |
535 | if (info.gamepadMaxButton != QGamepadManager::ButtonInvalid |
536 | && val == 1.0) { |
537 | info.gamepadLastButton = info.gamepadMaxButton; |
538 | emit m_backend->gamepadButtonPressed(deviceId: m_productId, button: info.gamepadMaxButton, value: val); |
539 | } else if (info.gamepadMinButton != QGamepadManager::ButtonInvalid |
540 | && val == -1.0) { |
541 | info.gamepadLastButton = info.gamepadMinButton; |
542 | emit m_backend->gamepadButtonPressed(deviceId: m_productId, button: info.gamepadMinButton, value: -val); |
543 | } else if (!val && info.gamepadLastButton != QGamepadManager::ButtonInvalid) { |
544 | QGamepadManager::GamepadButton but = info.gamepadLastButton; |
545 | info.gamepadLastButton = QGamepadManager::ButtonInvalid; |
546 | emit m_backend->gamepadButtonReleased(deviceId: m_productId, button: but); |
547 | } |
548 | } |
549 | } |
550 | } |
551 | |
552 | QT_END_NAMESPACE |
553 |
Definitions
- lcEGB
- EvdevAxisInfo
- EvdevAxisInfo
- normalized
- setAbsInfo
- restoreSavedData
- dataToSave
- QEvdevGamepadBackend
- start
- newDevice
- device
- resetConfiguration
- isConfigurationNeeded
- configureButton
- configureAxis
- setCancelConfigureButton
- stop
- handleAddedDevice
- handleRemovedDevice
- QEvdevGamepadDevice
- ~QEvdevGamepadDevice
- resetConfiguration
- isConfigurationNeeded
- configureButton
- configureAxis
- setCancelConfigureButton
- openDevice
- operator<<
- readData
- saveData
Learn Advanced QML with KDAB
Find out more