aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/gamepads
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/gamepads')
-rw-r--r--src/plugins/gamepads/evdev/evdev.json3
-rw-r--r--src/plugins/gamepads/evdev/evdev.pro14
-rw-r--r--src/plugins/gamepads/evdev/main.cpp62
-rw-r--r--src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp404
-rw-r--r--src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h115
-rw-r--r--src/plugins/gamepads/gamepads.pro4
-rw-r--r--src/plugins/gamepads/sdl2/main.cpp61
-rw-r--r--src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp186
-rw-r--r--src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h74
-rw-r--r--src/plugins/gamepads/sdl2/sdl2.json3
-rw-r--r--src/plugins/gamepads/sdl2/sdl2.pro27
-rw-r--r--src/plugins/gamepads/xinput/main.cpp62
-rw-r--r--src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp291
-rw-r--r--src/plugins/gamepads/xinput/qxinputgamepadbackend_p.h63
-rw-r--r--src/plugins/gamepads/xinput/xinput.json3
-rw-r--r--src/plugins/gamepads/xinput/xinput.pro14
16 files changed, 1386 insertions, 0 deletions
diff --git a/src/plugins/gamepads/evdev/evdev.json b/src/plugins/gamepads/evdev/evdev.json
new file mode 100644
index 0000000..90c6d7d
--- /dev/null
+++ b/src/plugins/gamepads/evdev/evdev.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "evdev" ]
+}
diff --git a/src/plugins/gamepads/evdev/evdev.pro b/src/plugins/gamepads/evdev/evdev.pro
new file mode 100644
index 0000000..7263f5c
--- /dev/null
+++ b/src/plugins/gamepads/evdev/evdev.pro
@@ -0,0 +1,14 @@
+TARGET = evdevgamepad
+QT += core-private platformsupport-private gamepad gamepad-private
+
+PLUGIN_TYPE = gamepads
+PLUGIN_CLASS_NAME = QEvdevGamepadBackendPlugin
+load(qt_plugin)
+
+HEADERS += qevdevgamepadbackend_p.h
+SOURCES += \
+ qevdevgamepadbackend.cpp \
+ main.cpp
+
+OTHER_FILES += \
+ evdev.json
diff --git a/src/plugins/gamepads/evdev/main.cpp b/src/plugins/gamepads/evdev/main.cpp
new file mode 100644
index 0000000..64f6952
--- /dev/null
+++ b/src/plugins/gamepads/evdev/main.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGamepad/private/qgamepadbackendfactory_p.h>
+#include <QtGamepad/private/qgamepadbackendplugin_p.h>
+
+#include "qevdevgamepadbackend_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEvdevGamepadBackendPlugin : public QGamepadBackendPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "evdev.json")
+public:
+ QGamepadBackend *create(const QString &key, const QStringList &paramList) Q_DECL_OVERRIDE;
+};
+
+QGamepadBackend *QEvdevGamepadBackendPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(key)
+ Q_UNUSED(paramList)
+
+ return new QEvdevGamepadBackend;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp b/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp
new file mode 100644
index 0000000..d0c4a3f
--- /dev/null
+++ b/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qevdevgamepadbackend_p.h"
+#include <QtCore/QSocketNotifier>
+#include <QtCore/QLoggingCategory>
+#include <QtPlatformSupport/private/qdevicediscovery_p.h>
+#include <QtCore/private/qcore_unix_p.h>
+#include <linux/input.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcEGB, "qt.gamepad")
+
+QEvdevGamepadBackend::QEvdevGamepadBackend()
+ : m_nextIndex(0)
+{
+}
+
+bool QEvdevGamepadBackend::start()
+{
+ qCDebug(lcEGB) << "start";
+ QByteArray device = qgetenv("QT_GAMEPAD_DEVICE");
+ if (device.isEmpty()) {
+ qCDebug(lcEGB) << "Using device discovery";
+ m_discovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_Joystick, this);
+ if (m_discovery) {
+ QStringList devices = m_discovery->scanConnectedDevices();
+ foreach (const QString &devStr, devices) {
+ device = devStr.toUtf8();
+ m_devices.append(newDevice(device));
+ }
+ connect(m_discovery, SIGNAL(deviceDetected(QString)), this, SLOT(handleAddedDevice(QString)));
+ connect(m_discovery, SIGNAL(deviceRemoved(QString)), this, SLOT(handleRemovedDevice(QString)));
+ } else {
+ qWarning("No device specified, set QT_GAMEPAD_DEVICE");
+ return false;
+ }
+ } else {
+ qCDebug(lcEGB) << "Using device" << device;
+ m_devices.append(newDevice(device));
+ }
+
+ return true;
+}
+
+QEvdevGamepadDevice *QEvdevGamepadBackend::newDevice(const QByteArray &device)
+{
+ qCDebug(lcEGB) << "Opening device" << device;
+ return new QEvdevGamepadDevice(device, this);
+}
+
+// To be called only when it is sure that there is a controller on-line.
+int QEvdevGamepadBackend::indexForDevice(const QByteArray &device)
+{
+ int index;
+ if (m_devIndex.contains(device)) {
+ index = m_devIndex[device];
+ } else {
+ index = m_nextIndex++;
+ m_devIndex[device] = index;
+ }
+ return index;
+}
+
+void QEvdevGamepadBackend::stop()
+{
+ qCDebug(lcEGB) << "stop";
+ qDeleteAll(m_devices);
+ m_devices.clear();
+}
+
+void QEvdevGamepadBackend::handleAddedDevice(const QString &device)
+{
+ // This does not imply that an actual controller is connected.
+ // When connecting the wireless receiver 4 devices will show up right away.
+ // Therefore gamepadAdded() will be emitted only later, when something is read.
+ qCDebug(lcEGB) << "Connected device" << device;
+ m_devices.append(newDevice(device.toUtf8()));
+}
+
+void QEvdevGamepadBackend::handleRemovedDevice(const QString &device)
+{
+ qCDebug(lcEGB) << "Disconnected device" << device;
+ QByteArray dev = device.toUtf8();
+ for (int i = 0; i < m_devices.count(); ++i) {
+ if (m_devices[i]->deviceName() == dev) {
+ delete m_devices[i];
+ m_devices.removeAt(i);
+ break;
+ }
+ }
+}
+
+QEvdevGamepadDevice::QEvdevGamepadDevice(const QByteArray &dev, QEvdevGamepadBackend *backend)
+ : m_dev(dev),
+ m_backend(backend),
+ m_index(-1),
+ m_fd(-1),
+ m_notifier(0),
+ m_prevYHatButton(QGamepadManager::ButtonInvalid),
+ m_prevXHatButton(QGamepadManager::ButtonInvalid)
+{
+ openDevice(dev);
+}
+
+QEvdevGamepadDevice::~QEvdevGamepadDevice()
+{
+ if (m_fd != -1)
+ QT_CLOSE(m_fd);
+ if (m_index >= 0)
+ emit m_backend->gamepadRemoved(m_index);
+}
+
+bool QEvdevGamepadDevice::openDevice(const QByteArray &dev)
+{
+ m_fd = QT_OPEN(dev.constData(), O_RDONLY | O_NDELAY, 0);
+
+ if (m_fd >= 0) {
+ m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
+ connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readData()));
+ qCDebug(lcEGB) << "Successfully opened" << dev;
+ } else {
+ qErrnoWarning(errno, "Gamepad: Cannot open input device %s", qPrintable(dev));
+ return false;
+ }
+
+ // Defaults. To be replaced with queried values below.
+ m_axisInfo.insert(ABS_X, AxisInfo(-32768, 32767));
+ m_axisInfo.insert(ABS_Y, AxisInfo(-32768, 32767));
+ m_axisInfo.insert(ABS_RX, AxisInfo(-32768, 32767));
+ m_axisInfo.insert(ABS_RY, AxisInfo(-32768, 32767));
+ m_axisInfo.insert(ABS_Z, AxisInfo(0, 255));
+ m_axisInfo.insert(ABS_RZ, AxisInfo(0, 255));
+
+ input_absinfo absInfo;
+ memset(&absInfo, 0, sizeof(input_absinfo));
+ if (ioctl(m_fd, EVIOCGABS(ABS_X), &absInfo) >= 0)
+ m_axisInfo.insert(ABS_X, AxisInfo(absInfo.minimum, absInfo.maximum));
+ if (ioctl(m_fd, EVIOCGABS(ABS_Y), &absInfo) >= 0)
+ m_axisInfo.insert(ABS_Y, AxisInfo(absInfo.minimum, absInfo.maximum));
+ if (ioctl(m_fd, EVIOCGABS(ABS_RX), &absInfo) >= 0)
+ m_axisInfo.insert(ABS_RX, AxisInfo(absInfo.minimum, absInfo.maximum));
+ if (ioctl(m_fd, EVIOCGABS(ABS_RY), &absInfo) >= 0)
+ m_axisInfo.insert(ABS_RY, AxisInfo(absInfo.minimum, absInfo.maximum));
+ if (ioctl(m_fd, EVIOCGABS(ABS_Z), &absInfo) >= 0)
+ m_axisInfo.insert(ABS_Z, AxisInfo(absInfo.minimum, absInfo.maximum));
+ if (ioctl(m_fd, EVIOCGABS(ABS_RZ), &absInfo) >= 0)
+ m_axisInfo.insert(ABS_RZ, AxisInfo(absInfo.minimum, absInfo.maximum));
+
+ qCDebug(lcEGB) << "Axis limits:" << m_axisInfo;
+
+ return true;
+}
+
+QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::AxisInfo &axisInfo)
+{
+ dbg.nospace() << "AxisInfo(min=" << axisInfo.minValue << ", max=" << axisInfo.maxValue << ")";
+ return dbg.space();
+}
+
+void QEvdevGamepadDevice::readData()
+{
+ input_event buffer[32];
+ int events = 0, n = 0;
+ for (; ;) {
+ events = QT_READ(m_fd, reinterpret_cast<char*>(buffer) + n, sizeof(buffer) - n);
+ if (events <= 0)
+ goto err;
+ n += events;
+ if (n % sizeof(::input_event) == 0)
+ break;
+ }
+
+ n /= sizeof(::input_event);
+
+ for (int i = 0; i < n; ++i)
+ processInputEvent(&buffer[i]);
+
+ return;
+
+err:
+ if (!events) {
+ qWarning("Gamepad: Got EOF from input device");
+ return;
+ } else if (events < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ qErrnoWarning(errno, "Gamepad: Could not read from input device");
+ if (errno == ENODEV) { // device got disconnected -> stop reading
+ delete m_notifier;
+ m_notifier = 0;
+ QT_CLOSE(m_fd);
+ m_fd = -1;
+ }
+ }
+ }
+}
+
+double QEvdevGamepadDevice::AxisInfo::normalized(int value) const
+{
+ if (minValue >= 0)
+ return (value - minValue) / double(maxValue - minValue);
+ else
+ return 2 * (value - minValue) / double(maxValue - minValue) - 1.0;
+}
+
+void QEvdevGamepadDevice::processInputEvent(input_event *e)
+{
+ if (m_index < 0) {
+ m_index = m_backend->indexForDevice(m_dev);
+ qCDebug(lcEGB) << "Adding gamepad" << m_dev << "with index" << m_index;
+ emit m_backend->gamepadAdded(m_index);
+ }
+
+ if (e->type == EV_KEY) {
+ const bool pressed = e->value;
+ QGamepadManager::GamepadButton btn = QGamepadManager::ButtonInvalid;
+
+ switch (e->code) {
+ case BTN_START:
+ btn = QGamepadManager::ButtonStart;
+ break;
+ case BTN_SELECT:
+ btn = QGamepadManager::ButtonSelect;
+ break;
+ case BTN_MODE:
+ btn = QGamepadManager::ButtonGuide;
+ break;
+
+ case BTN_X:
+ btn = QGamepadManager::ButtonX;
+ break;
+ case BTN_Y:
+ btn = QGamepadManager::ButtonY;
+ break;
+ case BTN_A:
+ btn = QGamepadManager::ButtonA;
+ break;
+ case BTN_B:
+ btn = QGamepadManager::ButtonB;
+ break;
+
+ case BTN_TL:
+ btn = QGamepadManager::ButtonL1;
+ break;
+ case BTN_TR:
+ btn = QGamepadManager::ButtonR1;
+ break;
+
+ case BTN_THUMB: // wireless
+ case BTN_THUMBL: // wired
+ btn = QGamepadManager::ButtonL3;
+ break;
+ case BTN_THUMBR:
+ btn = QGamepadManager::ButtonR3;
+ break;
+
+ // Directional buttons for wireless
+ case BTN_TRIGGER_HAPPY1:
+ btn = QGamepadManager::ButtonLeft;
+ break;
+ case BTN_TRIGGER_HAPPY2:
+ btn = QGamepadManager::ButtonRight;
+ break;
+ case BTN_TRIGGER_HAPPY3:
+ btn = QGamepadManager::ButtonUp;
+ break;
+ case BTN_TRIGGER_HAPPY4:
+ btn = QGamepadManager::ButtonDown;
+ break;
+
+ default:
+ break;
+ }
+ if (btn != QGamepadManager::ButtonInvalid) {
+ if (pressed)
+ emit m_backend->gamepadButtonPressed(m_index, btn, 1.0);
+ else
+ emit m_backend->gamepadButtonReleased(m_index, btn);
+ }
+ } else if (e->type == EV_ABS) {
+ if (e->code == ABS_HAT0X || e->code == ABS_HAT0Y) {
+ //Special logic for digital direction buttons as it is treated as a hat axis
+ //with the wired controller.
+ double value = m_axisInfo.value(e->code).normalized(e->value);
+ QGamepadManager::GamepadButton btn = QGamepadManager::ButtonInvalid;
+ bool pressed = false;
+ if (e->code == ABS_HAT0X) {
+ if (value == 1.0) {
+ btn = QGamepadManager::ButtonRight;
+ m_prevXHatButton = btn;
+ pressed = true;
+ } else if (value == -1.0) {
+ btn = QGamepadManager::ButtonLeft;
+ m_prevXHatButton = btn;
+ pressed = true;
+ } else {
+ //Release
+ btn = m_prevXHatButton;
+ m_prevXHatButton = QGamepadManager::ButtonInvalid;
+ pressed = false;
+ }
+ } else {
+ if (value == 1.0) {
+ btn = QGamepadManager::ButtonDown;
+ m_prevYHatButton = btn;
+ pressed = true;
+ } else if (value == -1.0) {
+ btn = QGamepadManager::ButtonUp;
+ m_prevYHatButton = btn;
+ pressed = true;
+ } else {
+ //Release
+ btn = m_prevYHatButton;
+ m_prevYHatButton = QGamepadManager::ButtonInvalid;
+ pressed = false;
+ }
+ }
+ if (btn != QGamepadManager::ButtonInvalid) {
+ if (pressed)
+ emit m_backend->gamepadButtonPressed(m_index, btn, 1.0);
+ else
+ emit m_backend->gamepadButtonReleased(m_index, btn);
+ }
+
+ } else {
+ QGamepadManager::GamepadAxis axis = QGamepadManager::AxisInvalid;
+ QGamepadManager::GamepadButton btn = QGamepadManager::ButtonInvalid;
+
+ switch (e->code) {
+ case ABS_X:
+ axis = QGamepadManager::AxisLeftX;
+ break;
+ case ABS_Y:
+ axis = QGamepadManager::AxisLeftY;
+ break;
+
+ case ABS_RX:
+ axis = QGamepadManager::AxisRightX;
+ break;
+ case ABS_RY:
+ axis = QGamepadManager::AxisRightY;
+ break;
+
+ case ABS_Z:
+ btn = QGamepadManager::ButtonL2;
+ break;
+ case ABS_RZ:
+ btn = QGamepadManager::ButtonR2;
+ break;
+
+ default:
+ break;
+ }
+
+ AxisInfo axisInfo = m_axisInfo.value(e->code);
+ const double value = axisInfo.normalized(e->value);
+
+ if (axis != QGamepadManager::AxisInvalid) {
+ emit m_backend->gamepadAxisMoved(m_index, axis, value);
+ } else if (btn != QGamepadManager::ButtonInvalid) {
+ if (!qFuzzyIsNull(value))
+ emit m_backend->gamepadButtonPressed(m_index, btn, value);
+ else
+ emit m_backend->gamepadButtonReleased(m_index, btn);
+ }
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h b/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h
new file mode 100644
index 0000000..0371989
--- /dev/null
+++ b/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVDEVGAMEPADCONTROLLER_H
+#define QEVDEVGAMEPADCONTROLLER_H
+
+#include <QtGamepad/QGamepadManager>
+#include <QtGamepad/private/qgamepadbackend_p.h>
+#include <QtCore/QHash>
+#include <QtCore/QVector>
+
+struct input_event;
+
+QT_BEGIN_NAMESPACE
+
+class QSocketNotifier;
+class QDeviceDiscovery;
+class QEvdevGamepadBackend;
+
+class QEvdevGamepadDevice : public QObject
+{
+ Q_OBJECT
+
+public:
+ QEvdevGamepadDevice(const QByteArray &dev, QEvdevGamepadBackend *backend);
+ ~QEvdevGamepadDevice();
+ QByteArray deviceName() const { return m_dev; }
+
+private slots:
+ void readData();
+
+private:
+ void processInputEvent(input_event *e);
+ bool openDevice(const QByteArray &dev);
+
+ QByteArray m_dev;
+ QEvdevGamepadBackend *m_backend;
+ int m_index;
+ int m_fd;
+ QSocketNotifier *m_notifier;
+ struct AxisInfo {
+ AxisInfo() : minValue(0), maxValue(1) { }
+ AxisInfo(int minValue, int maxValue) : minValue(minValue), maxValue(maxValue) { }
+ double normalized(int value) const;
+
+ int minValue;
+ int maxValue;
+ };
+ QHash<int, AxisInfo> m_axisInfo;
+ QGamepadManager::GamepadButton m_prevYHatButton;
+ QGamepadManager::GamepadButton m_prevXHatButton;
+ friend QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::AxisInfo &axisInfo);
+};
+
+QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::AxisInfo &axisInfo);
+
+class QEvdevGamepadBackend : public QGamepadBackend
+{
+ Q_OBJECT
+
+public:
+ QEvdevGamepadBackend();
+ bool start() Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+ int indexForDevice(const QByteArray &device);
+
+private slots:
+ void handleAddedDevice(const QString &device);
+ void handleRemovedDevice(const QString &device);
+
+private:
+ QEvdevGamepadDevice *newDevice(const QByteArray &device);
+
+ QDeviceDiscovery *m_discovery;
+ QVector<QEvdevGamepadDevice *> m_devices;
+ QHash<QByteArray, int> m_devIndex;
+ int m_nextIndex;
+};
+
+QT_END_NAMESPACE
+
+#endif // QEVDEVGAMEPADCONTROLLER_H
diff --git a/src/plugins/gamepads/gamepads.pro b/src/plugins/gamepads/gamepads.pro
new file mode 100644
index 0000000..928b288
--- /dev/null
+++ b/src/plugins/gamepads/gamepads.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+config_sdl:SUBDIRS += sdl2
+contains(QT_CONFIG, evdev): SUBDIRS += evdev
+win32: !wince*: SUBDIRS += xinput
diff --git a/src/plugins/gamepads/sdl2/main.cpp b/src/plugins/gamepads/sdl2/main.cpp
new file mode 100644
index 0000000..cc28ad2
--- /dev/null
+++ b/src/plugins/gamepads/sdl2/main.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGamepad/private/qgamepadbackendfactory_p.h>
+#include <QtGamepad/private/qgamepadbackendplugin_p.h>
+
+#include "qsdlgamepadbackend_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSdl2GamepadBackendPlugin : public QGamepadBackendPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "sdl2.json")
+public:
+ QGamepadBackend *create(const QString &key, const QStringList &paramList) Q_DECL_OVERRIDE;
+};
+
+QGamepadBackend *QSdl2GamepadBackendPlugin::create(const QString &key, const QStringList &paramList) {
+ Q_UNUSED(key)
+ Q_UNUSED(paramList)
+
+ return new QSdlGamepadBackend();
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp b/src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp
new file mode 100644
index 0000000..791e383
--- /dev/null
+++ b/src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp
@@ -0,0 +1,186 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qsdlgamepadbackend_p.h"
+
+#include <QtCore/QDebug>
+
+#include <SDL.h>
+
+QT_BEGIN_NAMESPACE
+
+QSdlGamepadBackend::QSdlGamepadBackend(QObject *parent)
+ : QGamepadBackend(parent)
+{
+ connect(&m_eventLoopTimer, SIGNAL(timeout()), this, SLOT(pumpSdlEventLoop()));
+}
+
+QSdlGamepadBackend::~QSdlGamepadBackend()
+{
+}
+
+void QSdlGamepadBackend::pumpSdlEventLoop()
+{
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ if (event.type == SDL_CONTROLLERAXISMOTION) {
+ SDL_ControllerAxisEvent axisEvent = event.caxis;
+ //qDebug() << axisEvent.timestamp << "Axis Event: " << axisEvent.which << axisEvent.axis << axisEvent.value;
+ double value;
+ if (axisEvent.value >= 0)
+ value = axisEvent.value / 32767.0;
+ else
+ value = axisEvent.value / 32768.0;
+ switch (axisEvent.axis) {
+ case SDL_CONTROLLER_AXIS_LEFTX:
+ emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisLeftX, value);
+ break;
+ case SDL_CONTROLLER_AXIS_LEFTY:
+ emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisLeftY, value);
+ break;
+ case SDL_CONTROLLER_AXIS_RIGHTX:
+ emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisRightX, value);
+ break;
+ case SDL_CONTROLLER_AXIS_RIGHTY:
+ emit gamepadAxisMoved(m_instanceIdForIndex[axisEvent.which], QGamepadManager::AxisRightY, value);
+ break;
+ case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
+ if (value == 0)
+ emit gamepadButtonReleased(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonL2);
+ else
+ emit gamepadButtonPressed(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonL2, value);
+ break;
+ case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
+ if (value == 0)
+ emit gamepadButtonReleased(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonR2);
+ else
+ emit gamepadButtonPressed(m_instanceIdForIndex[axisEvent.which], QGamepadManager::ButtonR2, value);
+ break;
+ default:
+ break;
+ }
+
+ } else if (event.type == SDL_CONTROLLERBUTTONDOWN) {
+ SDL_ControllerButtonEvent buttonEvent = event.cbutton;
+ //qDebug() << buttonEvent.timestamp << "Button Press: " << buttonEvent.which << buttonEvent.button << buttonEvent.state;
+ emit gamepadButtonPressed(m_instanceIdForIndex[buttonEvent.which], translateButton(buttonEvent.button), 1.0);
+ } else if (event.type == SDL_CONTROLLERBUTTONUP) {
+ SDL_ControllerButtonEvent buttonEvent = event.cbutton;
+ //qDebug() << buttonEvent.timestamp << "Button Release: " << buttonEvent.which << buttonEvent.button << buttonEvent.state;
+ emit gamepadButtonReleased(m_instanceIdForIndex[buttonEvent.which], translateButton(buttonEvent.button));
+ } else if (event.type == SDL_CONTROLLERDEVICEADDED) {
+ SDL_ControllerDeviceEvent deviceEvent = event.cdevice;
+ //qDebug() << deviceEvent.timestamp << "Controller Added: " << deviceEvent.which;
+ SDL_GameController *controller = SDL_GameControllerOpen(deviceEvent.which);
+ if (controller) {
+ m_indexForController.insert(deviceEvent.which, controller);
+ int instanceID = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
+ m_instanceIdForIndex.insert(instanceID, deviceEvent.which);
+ //qDebug() << "Controller " << deviceEvent.which << " added with instanceId: " << instanceID;
+ emit gamepadAdded(deviceEvent.which);
+ }
+ } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
+ SDL_ControllerDeviceEvent deviceEvent = event.cdevice;
+
+ int index = m_instanceIdForIndex[deviceEvent.which];
+ SDL_GameControllerClose(m_indexForController[index]);
+ emit gamepadRemoved(index);
+ m_indexForController.remove(index);
+ m_instanceIdForIndex.remove(deviceEvent.which);
+
+ } else if (event.type == SDL_CONTROLLERDEVICEREMAPPED) {
+ //SDL_ControllerDeviceEvent deviceEvent = event.cdevice;
+ //qDebug() << deviceEvent.timestamp << "Controller Remapped: " << deviceEvent.which;
+ }
+ }
+}
+
+bool QSdlGamepadBackend::start()
+{
+ //Initialize SDL with necessary subsystems for gamepad support
+ if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK)) {
+ qDebug() << SDL_GetError();
+ return false;
+ }
+
+ m_eventLoopTimer.start(16);
+ return true;
+}
+
+void QSdlGamepadBackend::stop()
+{
+ m_eventLoopTimer.stop();
+ SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK);
+}
+
+QGamepadManager::GamepadButton QSdlGamepadBackend::translateButton(int button)
+{
+ switch (button) {
+ case SDL_CONTROLLER_BUTTON_A:
+ return QGamepadManager::ButtonA;
+ case SDL_CONTROLLER_BUTTON_B:
+ return QGamepadManager::ButtonB;
+ case SDL_CONTROLLER_BUTTON_X:
+ return QGamepadManager::ButtonX;
+ case SDL_CONTROLLER_BUTTON_Y:
+ return QGamepadManager::ButtonY;
+ case SDL_CONTROLLER_BUTTON_BACK:
+ return QGamepadManager::ButtonSelect;
+ case SDL_CONTROLLER_BUTTON_GUIDE:
+ return QGamepadManager::ButtonGuide;
+ case SDL_CONTROLLER_BUTTON_START:
+ return QGamepadManager::ButtonStart;
+ case SDL_CONTROLLER_BUTTON_LEFTSTICK:
+ return QGamepadManager::ButtonL3;
+ case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
+ return QGamepadManager::ButtonR3;
+ case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
+ return QGamepadManager::ButtonL1;
+ case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
+ return QGamepadManager::ButtonR1;
+ case SDL_CONTROLLER_BUTTON_DPAD_UP:
+ return QGamepadManager::ButtonUp;
+ case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
+ return QGamepadManager::ButtonDown;
+ case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
+ return QGamepadManager::ButtonLeft;
+ case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
+ return QGamepadManager::ButtonRight;
+ default:
+ return QGamepadManager::ButtonInvalid;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h b/src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h
new file mode 100644
index 0000000..44e81dc
--- /dev/null
+++ b/src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSDLGAMEPADCONTROLLER_H
+#define QSDLGAMEPADCONTROLLER_H
+
+#include <QtCore/QTimer>
+#include <QtCore/QMap>
+
+#include <SDL_gamecontroller.h>
+
+#include <QtGamepad/QGamepadManager>
+#include <QtGamepad/private/qgamepadbackend_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSdlGamepadBackend : public QGamepadBackend
+{
+ Q_OBJECT
+public:
+ explicit QSdlGamepadBackend(QObject *parent = 0);
+ ~QSdlGamepadBackend();
+
+private slots:
+ void pumpSdlEventLoop();
+
+protected:
+ bool start();
+ void stop();
+
+private:
+ QGamepadManager::GamepadButton translateButton(int button);
+
+ QTimer m_eventLoopTimer;
+ QMap<int, SDL_GameController*> m_indexForController;
+ QMap<int, int> m_instanceIdForIndex;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSDLGAMEPADCONTROLLER_H
diff --git a/src/plugins/gamepads/sdl2/sdl2.json b/src/plugins/gamepads/sdl2/sdl2.json
new file mode 100644
index 0000000..d2bd585
--- /dev/null
+++ b/src/plugins/gamepads/sdl2/sdl2.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "sdl2" ]
+}
diff --git a/src/plugins/gamepads/sdl2/sdl2.pro b/src/plugins/gamepads/sdl2/sdl2.pro
new file mode 100644
index 0000000..3d53711
--- /dev/null
+++ b/src/plugins/gamepads/sdl2/sdl2.pro
@@ -0,0 +1,27 @@
+TARGET = sdl2gamepad
+QT += gamepad gamepad-private
+
+PLUGIN_TYPE = gamepads
+PLUGIN_CLASS_NAME = QSdl2GamepadBackendPlugin
+load(qt_plugin)
+
+osx {
+ INCLUDEPATH += /Library/Frameworks/SDL2.framework/Headers
+ LIBS += -F/Library/Frameworks/ -framework SDL2
+}
+
+unix:!osx{
+ CONFIG += link_pkgconfig
+ PKGCONFIG += sdl2
+}
+
+win32: LIBS += -lSDL2
+
+HEADERS += qsdlgamepadbackend_p.h
+SOURCES += \
+ qsdlgamepadbackend.cpp \
+ main.cpp
+
+OTHER_FILES += \
+ sdl2.json
+
diff --git a/src/plugins/gamepads/xinput/main.cpp b/src/plugins/gamepads/xinput/main.cpp
new file mode 100644
index 0000000..a5eb0f8
--- /dev/null
+++ b/src/plugins/gamepads/xinput/main.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGamepad/private/qgamepadbackendfactory_p.h>
+#include <QtGamepad/private/qgamepadbackendplugin_p.h>
+
+#include "qxinputgamepadbackend_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QXInputGamepadBackendPlugin : public QGamepadBackendPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "xinput.json")
+public:
+ QGamepadBackend *create(const QString &key, const QStringList &paramList) Q_DECL_OVERRIDE;
+};
+
+QGamepadBackend *QXInputGamepadBackendPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(key)
+ Q_UNUSED(paramList)
+
+ return new QXInputGamepadBackend;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp b/src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp
new file mode 100644
index 0000000..97680cb
--- /dev/null
+++ b/src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qxinputgamepadbackend_p.h"
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QThread>
+#include <qmath.h>
+#include <windows.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcXGB, "qt.gamepad")
+
+#define POLL_SLEEP_MS 5
+#define POLL_SLOT_CHECK_MS 4000
+
+#define XUSER_MAX_COUNT 4
+
+#define XINPUT_GAMEPAD_DPAD_UP 0x0001
+#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
+#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
+#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
+#define XINPUT_GAMEPAD_START 0x0010
+#define XINPUT_GAMEPAD_BACK 0x0020
+#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
+#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
+#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
+#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
+#define XINPUT_GAMEPAD_A 0x1000
+#define XINPUT_GAMEPAD_B 0x2000
+#define XINPUT_GAMEPAD_X 0x4000
+#define XINPUT_GAMEPAD_Y 0x8000
+
+#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
+#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
+#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
+
+struct XINPUT_GAMEPAD
+{
+ unsigned short wButtons;
+ unsigned char bLeftTrigger;
+ unsigned char bRightTrigger;
+ short sThumbLX;
+ short sThumbLY;
+ short sThumbRX;
+ short sThumbRY;
+};
+
+struct XINPUT_STATE
+{
+ unsigned long dwPacketNumber;
+ XINPUT_GAMEPAD Gamepad;
+};
+
+typedef DWORD (WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState);
+static XInputGetState_t XInputGetState;
+
+class QXInputThread : public QThread
+{
+public:
+ QXInputThread(QXInputGamepadBackend *backend);
+ void run() Q_DECL_OVERRIDE;
+ void signalQuit() { m_quit.fetchAndStoreAcquire(1); }
+
+private:
+ void dispatch(int idx, XINPUT_GAMEPAD *state);
+
+ QXInputGamepadBackend *m_backend;
+ QAtomicInt m_quit;
+ struct Controller {
+ bool connected;
+ int skippedPolls;
+ unsigned long lastPacketNumber;
+ // State cache. Only want to emit signals for values that really change.
+ unsigned short buttons;
+ unsigned char triggers[2];
+ double axis[2][2];
+ } m_controllers[XUSER_MAX_COUNT];
+};
+
+QXInputThread::QXInputThread(QXInputGamepadBackend *backend)
+ : m_backend(backend),
+ m_quit(false)
+{
+ memset(m_controllers, 0, sizeof(m_controllers));
+}
+
+void QXInputThread::dispatch(int idx, XINPUT_GAMEPAD *state)
+{
+ static const struct ButtonMap {
+ unsigned short xbutton;
+ QGamepadManager::GamepadButton qbutton;
+ } buttonMap[] = {
+ { XINPUT_GAMEPAD_DPAD_UP, QGamepadManager::ButtonUp },
+ { XINPUT_GAMEPAD_DPAD_DOWN, QGamepadManager::ButtonDown },
+ { XINPUT_GAMEPAD_DPAD_LEFT, QGamepadManager::ButtonLeft },
+ { XINPUT_GAMEPAD_DPAD_RIGHT, QGamepadManager::ButtonRight },
+ { XINPUT_GAMEPAD_START, QGamepadManager::ButtonStart },
+ { XINPUT_GAMEPAD_BACK, QGamepadManager::ButtonSelect },
+ { XINPUT_GAMEPAD_LEFT_SHOULDER, QGamepadManager::ButtonL1 },
+ { XINPUT_GAMEPAD_RIGHT_SHOULDER, QGamepadManager::ButtonR1 },
+ { XINPUT_GAMEPAD_LEFT_THUMB, QGamepadManager::ButtonL3 },
+ { XINPUT_GAMEPAD_RIGHT_THUMB, QGamepadManager::ButtonR3 },
+ { XINPUT_GAMEPAD_A, QGamepadManager::ButtonA },
+ { XINPUT_GAMEPAD_B, QGamepadManager::ButtonB },
+ { XINPUT_GAMEPAD_X, QGamepadManager::ButtonX },
+ { XINPUT_GAMEPAD_Y, QGamepadManager::ButtonY }
+ };
+ for (int i = 0; i < sizeof(buttonMap) / sizeof(ButtonMap); ++i) {
+ const unsigned short xb = buttonMap[i].xbutton;
+ unsigned short isDown = state->wButtons & xb;
+ if (isDown != (m_controllers[idx].buttons & xb)) {
+ if (isDown) {
+ m_controllers[idx].buttons |= xb;
+ emit m_backend->gamepadButtonPressed(idx, buttonMap[i].qbutton, 1);
+ } else {
+ m_controllers[idx].buttons &= ~xb;
+ emit m_backend->gamepadButtonReleased(idx, buttonMap[i].qbutton);
+ }
+ }
+ }
+
+ if (m_controllers[idx].triggers[0] != state->bLeftTrigger) {
+ m_controllers[idx].triggers[0] = state->bLeftTrigger;
+ const double value = state->bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD
+ ? (state->bLeftTrigger - XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
+ / (255.0 - XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
+ : 0.0;
+ if (!qFuzzyIsNull(value))
+ emit m_backend->gamepadButtonPressed(idx, QGamepadManager::ButtonL2, value);
+ else
+ emit m_backend->gamepadButtonReleased(idx, QGamepadManager::ButtonL2);
+ }
+ if (m_controllers[idx].triggers[1] != state->bRightTrigger) {
+ m_controllers[idx].triggers[1] = state->bRightTrigger;
+ const double value = state->bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD
+ ? (state->bRightTrigger - XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
+ / (255.0 - XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
+ : 0.0;
+ if (!qFuzzyIsNull(value))
+ emit m_backend->gamepadButtonPressed(idx, QGamepadManager::ButtonR2, value);
+ else
+ emit m_backend->gamepadButtonReleased(idx, QGamepadManager::ButtonR2);
+ }
+
+ double x, y;
+ if (qSqrt(state->sThumbLX * state->sThumbLX + state->sThumbLY * state->sThumbLY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
+ x = 2 * (state->sThumbLX + 32768.0) / 65535.0 - 1.0;
+ y = 2 * (-state->sThumbLY + 32768.0) / 65535.0 - 1.0;
+ } else {
+ x = y = 0;
+ }
+ if (m_controllers[idx].axis[0][0] != x) {
+ m_controllers[idx].axis[0][0] = x;
+ emit m_backend->gamepadAxisMoved(idx, QGamepadManager::AxisLeftX, x);
+ }
+ if (m_controllers[idx].axis[0][1] != y) {
+ m_controllers[idx].axis[0][1] = y;
+ emit m_backend->gamepadAxisMoved(idx, QGamepadManager::AxisLeftY, y);
+ }
+ if (qSqrt(state->sThumbRX * state->sThumbRX + state->sThumbRY * state->sThumbRY) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) {
+ x = 2 * (state->sThumbRX + 32768.0) / 65535.0 - 1.0;
+ y = 2 * (-state->sThumbRY + 32768.0) / 65535.0 - 1.0;
+ } else {
+ x = y = 0;
+ }
+ if (m_controllers[idx].axis[1][0] != x) {
+ m_controllers[idx].axis[1][0] = x;
+ emit m_backend->gamepadAxisMoved(idx, QGamepadManager::AxisRightX, x);
+ }
+ if (m_controllers[idx].axis[1][1] != y) {
+ m_controllers[idx].axis[1][1] = y;
+ emit m_backend->gamepadAxisMoved(idx, QGamepadManager::AxisRightY, y);
+ }
+}
+
+void QXInputThread::run()
+{
+ qCDebug(lcXGB, "XInput thread running");
+ bool firstPoll = true;
+ while (!m_quit.testAndSetAcquire(1, 0)) {
+ for (int i = 0; i < XUSER_MAX_COUNT; ++i) {
+ Controller *controller = m_controllers + i;
+
+ if (!firstPoll && !controller->connected && controller->skippedPolls < POLL_SLOT_CHECK_MS / POLL_SLEEP_MS) {
+ controller->skippedPolls++;
+ continue;
+ }
+
+ firstPoll = false;
+ controller->skippedPolls = 0;
+ XINPUT_STATE state;
+ memset(&state, 0, sizeof(state));
+
+ if (XInputGetState(i, &state) == ERROR_SUCCESS) {
+ if (controller->connected) {
+ if (controller->lastPacketNumber != state.dwPacketNumber) {
+ controller->lastPacketNumber = state.dwPacketNumber;
+ dispatch(i, &state.Gamepad);
+ }
+ } else {
+ controller->connected = true;
+ controller->lastPacketNumber = state.dwPacketNumber;
+ emit m_backend->gamepadAdded(i);
+ dispatch(i, &state.Gamepad);
+ }
+ } else {
+ if (controller->connected) {
+ controller->connected = false;
+ emit m_backend->gamepadRemoved(i);
+ }
+ }
+ }
+
+ Sleep(POLL_SLEEP_MS);
+ }
+ qCDebug(lcXGB, "XInput thread stopping");
+}
+
+QXInputGamepadBackend::QXInputGamepadBackend()
+ : m_thread(0)
+{
+}
+
+bool QXInputGamepadBackend::start()
+{
+ qCDebug(lcXGB) << "start";
+
+ m_lib.setFileName(QStringLiteral("xinput1_4.dll"));
+ if (!m_lib.load()) {
+ m_lib.setFileName(QStringLiteral("xinput1_3.dll"));
+ m_lib.load();
+ }
+
+ if (m_lib.isLoaded()) {
+ qCDebug(lcXGB, "Loaded XInput library %s", qPrintable(m_lib.fileName()));
+ XInputGetState = (XInputGetState_t) m_lib.resolve("XInputGetState");
+ if (XInputGetState) {
+ m_thread = new QXInputThread(this);
+ m_thread->start();
+ } else {
+ qWarning("Failed to resolve XInputGetState");
+ }
+ } else {
+ qWarning("Failed to load XInput library %s", qPrintable(m_lib.fileName()));
+ }
+
+ return m_lib.isLoaded();
+}
+
+void QXInputGamepadBackend::stop()
+{
+ qCDebug(lcXGB) << "stop";
+ m_thread->signalQuit();
+ m_thread->wait();
+ XInputGetState = 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/gamepads/xinput/qxinputgamepadbackend_p.h b/src/plugins/gamepads/xinput/qxinputgamepadbackend_p.h
new file mode 100644
index 0000000..42f7a4a
--- /dev/null
+++ b/src/plugins/gamepads/xinput/qxinputgamepadbackend_p.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Gamepad module
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QXINPUTGAMEPADCONTROLLER_H
+#define QXINPUTGAMEPADCONTROLLER_H
+
+#include <QtGamepad/QGamepadManager>
+#include <QtGamepad/private/qgamepadbackend_p.h>
+#include <QtCore/QLibrary>
+
+QT_BEGIN_NAMESPACE
+
+class QXInputThread;
+
+class QXInputGamepadBackend : public QGamepadBackend
+{
+ Q_OBJECT
+
+public:
+ QXInputGamepadBackend();
+ bool start() Q_DECL_OVERRIDE;
+ void stop() Q_DECL_OVERRIDE;
+
+private:
+ QXInputThread *m_thread;
+ QLibrary m_lib;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXINPUTGAMEPADCONTROLLER_H
diff --git a/src/plugins/gamepads/xinput/xinput.json b/src/plugins/gamepads/xinput/xinput.json
new file mode 100644
index 0000000..f8e3fb2
--- /dev/null
+++ b/src/plugins/gamepads/xinput/xinput.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "xinput" ]
+}
diff --git a/src/plugins/gamepads/xinput/xinput.pro b/src/plugins/gamepads/xinput/xinput.pro
new file mode 100644
index 0000000..5dac854
--- /dev/null
+++ b/src/plugins/gamepads/xinput/xinput.pro
@@ -0,0 +1,14 @@
+TARGET = xinputgamepad
+QT += gamepad gamepad-private
+
+PLUGIN_TYPE = gamepads
+PLUGIN_CLASS_NAME = QXInputGamepadBackendPlugin
+load(qt_plugin)
+
+HEADERS += qxinputgamepadbackend_p.h
+SOURCES += \
+ qxinputgamepadbackend.cpp \
+ main.cpp
+
+OTHER_FILES += \
+ xinput.json