diff options
Diffstat (limited to 'src/plugins/gamepads')
-rw-r--r-- | src/plugins/gamepads/evdev/evdev.json | 3 | ||||
-rw-r--r-- | src/plugins/gamepads/evdev/evdev.pro | 14 | ||||
-rw-r--r-- | src/plugins/gamepads/evdev/main.cpp | 62 | ||||
-rw-r--r-- | src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp | 404 | ||||
-rw-r--r-- | src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h | 115 | ||||
-rw-r--r-- | src/plugins/gamepads/gamepads.pro | 4 | ||||
-rw-r--r-- | src/plugins/gamepads/sdl2/main.cpp | 61 | ||||
-rw-r--r-- | src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp | 186 | ||||
-rw-r--r-- | src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h | 74 | ||||
-rw-r--r-- | src/plugins/gamepads/sdl2/sdl2.json | 3 | ||||
-rw-r--r-- | src/plugins/gamepads/sdl2/sdl2.pro | 27 | ||||
-rw-r--r-- | src/plugins/gamepads/xinput/main.cpp | 62 | ||||
-rw-r--r-- | src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp | 291 | ||||
-rw-r--r-- | src/plugins/gamepads/xinput/qxinputgamepadbackend_p.h | 63 | ||||
-rw-r--r-- | src/plugins/gamepads/xinput/xinput.json | 3 | ||||
-rw-r--r-- | src/plugins/gamepads/xinput/xinput.pro | 14 |
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 ¶mList) Q_DECL_OVERRIDE; +}; + +QGamepadBackend *QEvdevGamepadBackendPlugin::create(const QString &key, const QStringList ¶mList) +{ + 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 ¶mList) Q_DECL_OVERRIDE; +}; + +QGamepadBackend *QSdl2GamepadBackendPlugin::create(const QString &key, const QStringList ¶mList) { + 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 ¶mList) Q_DECL_OVERRIDE; +}; + +QGamepadBackend *QXInputGamepadBackendPlugin::create(const QString &key, const QStringList ¶mList) +{ + 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 |