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 
EvdevAxisInfo()66 QEvdevGamepadDevice::EvdevAxisInfo::EvdevAxisInfo()
67     : QGamepadBackend::AxisInfo<int>(0, 1, QGamepadManager::AxisInvalid)
68 {
69 }
70 
EvdevAxisInfo(int fd,quint16 abs,int min,int max,QGamepadManager::GamepadAxis gamepadAxis)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 
normalized(int value) const81 double QEvdevGamepadDevice::EvdevAxisInfo::normalized(int value) const
82 {
83     double val = AxisInfo::normalized(value);
84     if (qAbs(val) <= flat)
85         val = 0;
86     return val;
87 }
88 
setAbsInfo(int fd,int abs)89 void QEvdevGamepadDevice::EvdevAxisInfo::setAbsInfo(int fd, int abs)
90 {
91     input_absinfo absInfo;
92     memset(&absInfo, 0, sizeof(input_absinfo));
93     if (ioctl(fd, EVIOCGABS(abs), &absInfo) >= 0) {
94         minValue = absInfo.minimum;
95         maxValue = absInfo.maximum;
96         if (maxValue - minValue)
97             flat = std::abs(absInfo.flat / double(maxValue - minValue));
98     }
99 }
100 
restoreSavedData(int fd,int abs,const QVariantMap & value)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 
dataToSave() const109 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 
QEvdevGamepadBackend()118 QEvdevGamepadBackend::QEvdevGamepadBackend()
119 {
120 }
121 
start()122 bool QEvdevGamepadBackend::start()
123 {
124     qCDebug(lcEGB) << "start";
125     QByteArray device = qgetenv("QT_GAMEPAD_DEVICE");
126     if (device.isEmpty()) {
127         qCDebug(lcEGB) << "Using device discovery";
128         m_discovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Joystick, 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(newDevice(device));
134             }
135             connect(m_discovery, SIGNAL(deviceDetected(QString)), this, SLOT(handleAddedDevice(QString)));
136             connect(m_discovery, SIGNAL(deviceRemoved(QString)), this, SLOT(handleRemovedDevice(QString)));
137         } else {
138             qWarning("No device specified, set QT_GAMEPAD_DEVICE");
139             return false;
140         }
141     } else {
142         qCDebug(lcEGB) << "Using device" << device;
143         m_devices.append(newDevice(device));
144     }
145 
146     return true;
147 }
148 
newDevice(const QByteArray & device)149 QEvdevGamepadDevice *QEvdevGamepadBackend::newDevice(const QByteArray &device)
150 {
151     qCDebug(lcEGB) << "Opening device" << device;
152     return new QEvdevGamepadDevice(device, this);
153 }
154 
device(int deviceId)155 QEvdevGamepadDevice *QEvdevGamepadBackend::device(int deviceId)
156 {
157     for (QEvdevGamepadDevice *device : qAsConst(m_devices))
158         if (device->deviceId() == deviceId)
159             return device;
160     return nullptr;
161 }
162 
resetConfiguration(int deviceId)163 void QEvdevGamepadBackend::resetConfiguration(int deviceId)
164 {
165     if (QEvdevGamepadDevice *dev = device(deviceId))
166         return dev->resetConfiguration();
167 }
168 
isConfigurationNeeded(int deviceId)169 bool QEvdevGamepadBackend::isConfigurationNeeded(int deviceId)
170 {
171     if (QEvdevGamepadDevice *dev = device(deviceId))
172         return dev->isConfigurationNeeded();
173     return false;
174 }
175 
configureButton(int deviceId,QGamepadManager::GamepadButton button)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 
configureAxis(int deviceId,QGamepadManager::GamepadAxis axis)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 
setCancelConfigureButton(int deviceId,QGamepadManager::GamepadButton button)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 
stop()197 void QEvdevGamepadBackend::stop()
198 {
199     qCDebug(lcEGB) << "stop";
200     qDeleteAll(m_devices);
201     m_devices.clear();
202 }
203 
handleAddedDevice(const QString & device)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(newDevice(device.toUtf8()));
211 }
212 
handleRemovedDevice(const QString & device)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 
QEvdevGamepadDevice(const QByteArray & dev,QEvdevGamepadBackend * backend)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 
~QEvdevGamepadDevice()239 QEvdevGamepadDevice::~QEvdevGamepadDevice()
240 {
241     if (m_fd != -1)
242         QT_CLOSE(m_fd);
243 
244     if (m_productId)
245         emit m_backend->gamepadRemoved(m_productId);
246 }
247 
resetConfiguration()248 void QEvdevGamepadDevice::resetConfiguration()
249 {
250     m_axisMap.insert(ABS_X, EvdevAxisInfo(m_fd, ABS_X, -32768, 32767, QGamepadManager::AxisLeftX));
251     m_axisMap.insert(ABS_Y, EvdevAxisInfo(m_fd, ABS_Y, -32768, 32767, QGamepadManager::AxisLeftY));
252     m_axisMap.insert(ABS_RX, EvdevAxisInfo(m_fd, ABS_RX, -32768, 32767, QGamepadManager::AxisRightX));
253     m_axisMap.insert(ABS_RY, EvdevAxisInfo(m_fd, ABS_RY, -32768, 32767, QGamepadManager::AxisRightY));
254     m_axisMap.insert(ABS_Z, 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, 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, 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, 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(m_productId, QVariant());
290 }
291 
isConfigurationNeeded()292 bool QEvdevGamepadDevice::isConfigurationNeeded()
293 {
294     return m_needsConfigure;
295 }
296 
configureButton(QGamepadManager::GamepadButton button)297 bool QEvdevGamepadDevice::configureButton(QGamepadManager::GamepadButton button)
298 {
299     m_configureButton = button;
300     return true;
301 }
302 
configureAxis(QGamepadManager::GamepadAxis axis)303 bool QEvdevGamepadDevice::configureAxis(QGamepadManager::GamepadAxis axis)
304 {
305     m_configureAxis = axis;
306     return true;
307 }
308 
setCancelConfigureButton(QGamepadManager::GamepadButton button)309 bool QEvdevGamepadDevice::setCancelConfigureButton(QGamepadManager::GamepadButton button)
310 {
311     m_configureCancelButton = button;
312     return true;
313 }
314 
openDevice(const QByteArray & dev)315 bool QEvdevGamepadDevice::openDevice(const QByteArray &dev)
316 {
317     m_fd = QT_OPEN(dev.constData(), O_RDONLY | O_NDELAY, 0);
318 
319     if (m_fd >= 0) {
320         m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
321         connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readData()));
322         qCDebug(lcEGB) << "Successfully opened" << dev;
323     } else {
324         qErrnoWarning(errno, "Gamepad: Cannot open input device %s", qPrintable(dev));
325         return false;
326     }
327 
328     input_id id;
329     if (ioctl(m_fd, EVIOCGID, &id) >= 0) {
330         m_productId = id.product;
331 
332         QVariant settings = m_backend->readSettings(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(m_fd, key, 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(m_productId);
347 
348         // same as libevdev::libevdev_set_fd() in libevdev.c
349         char buffer[256];
350         memset(buffer, 0, sizeof(buffer));
351         if (ioctl(m_fd, EVIOCGNAME(sizeof(buffer) - 1), buffer) >= 0)
352             emit m_backend->gamepadNamed(m_productId, QString::fromUtf8(buffer));
353 
354     } else {
355         QT_CLOSE(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 
operator <<(QDebug dbg,const QEvdevGamepadDevice::EvdevAxisInfo & axisInfo)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 
readData()374 void QEvdevGamepadDevice::readData()
375 {
376     input_event buffer[32];
377     int events = 0, n = 0;
378     for (; ;) {
379         events = QT_READ(m_fd, reinterpret_cast<char*>(buffer) + n, 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(&buffer[i]);
391 
392     return;
393 
394 err:
395     if (!events) {
396         qWarning("Gamepad: Got EOF from input device");
397         return;
398     } else if (events < 0) {
399         if (errno != EINTR && errno != EAGAIN) {
400             qErrnoWarning(errno, "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(m_fd);
405                 m_fd = -1;
406             }
407         }
408     }
409 }
410 
saveData()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(m_productId, settings);
428 }
429 
processInputEvent(input_event * e)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(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(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(m_productId, but);
456         }
457 
458         it = m_buttonsMap.find(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(m_productId, btn, 1.0);
465             else
466                 emit m_backend->gamepadButtonReleased(m_productId, 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(inf.normalized(e->value)) == 1) {
472                 m_axisMap.insert(e->code, 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(m_productId, axis);
479             } else {
480                 return;
481             }
482         }
483 
484         AxisMap::iterator it = m_axisMap.find(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(e->code, axisInfo);
512                 saveData();
513                 emit m_backend->buttonConfigured(m_productId, but);
514             }
515         }
516 
517         it = m_axisMap.find(e->code);
518         if (it == m_axisMap.end())
519             return;
520 
521         EvdevAxisInfo &info = it.value();
522 
523         double val = info.normalized(e->value);
524 
525         if (info.gamepadAxis != QGamepadManager::AxisInvalid)
526             emit m_backend->gamepadAxisMoved(m_productId, info.gamepadAxis, val);
527 
528         if (info.gamepadMaxButton == info.gamepadMinButton &&
529                 info.gamepadMaxButton != QGamepadManager::ButtonInvalid) {
530             if (val)
531                 emit m_backend->gamepadButtonPressed(m_productId, info.gamepadMaxButton, std::abs(val));
532             else
533                 emit m_backend->gamepadButtonReleased(m_productId, info.gamepadMaxButton);
534         } else {
535             if (info.gamepadMaxButton != QGamepadManager::ButtonInvalid
536                     && val == 1.0) {
537                 info.gamepadLastButton = info.gamepadMaxButton;
538                 emit m_backend->gamepadButtonPressed(m_productId, info.gamepadMaxButton, val);
539             } else if (info.gamepadMinButton != QGamepadManager::ButtonInvalid
540                        && val == -1.0) {
541                 info.gamepadLastButton = info.gamepadMinButton;
542                 emit m_backend->gamepadButtonPressed(m_productId, info.gamepadMinButton, -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(m_productId, but);
547             }
548         }
549     }
550 }
551 
552 QT_END_NAMESPACE
553