aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBogDan Vatra <bogdan@kdab.com>2015-10-26 12:38:33 +0200
committerAndy Nichols <andy.nichols@theqtcompany.com>2015-10-26 12:40:37 +0000
commitc32e995bc79035e600893e91fdcec984f8293c75 (patch)
tree6de9a857b695fedfb3a52c5e4e34334759a09acc
parent00871e531f7fb44feb3dac7abfe4eff0af5dfada (diff)
Enable the usage of unknown gamepads.
Allowing the user to configure every button & axis of (s)his gamepad, will enable the usage of any gamepad. Currently only evdev backed has this feature. Change-Id: I6e0692980e77b448d20d5ba818b787a7b89e7257 Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
-rw-r--r--examples/configureButtons/configureButtons.pro19
-rw-r--r--examples/configureButtons/deployment.pri14
-rw-r--r--examples/configureButtons/main.cpp48
-rw-r--r--examples/configureButtons/main.qml616
-rw-r--r--examples/configureButtons/qml.qrc5
-rw-r--r--examples/examples.pro3
-rw-r--r--examples/keyNavigation/qml/main.qml7
-rw-r--r--examples/mouseItem/qml/main.qml7
-rw-r--r--examples/quickGamepad/qml/main.qml7
-rw-r--r--src/gamepad/qgamepadbackend.cpp56
-rw-r--r--src/gamepad/qgamepadbackend_p.h35
-rw-r--r--src/gamepad/qgamepadmanager.cpp33
-rw-r--r--src/gamepad/qgamepadmanager.h11
-rw-r--r--src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp494
-rw-r--r--src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h52
15 files changed, 1189 insertions, 218 deletions
diff --git a/examples/configureButtons/configureButtons.pro b/examples/configureButtons/configureButtons.pro
new file mode 100644
index 0000000..480f8fb
--- /dev/null
+++ b/examples/configureButtons/configureButtons.pro
@@ -0,0 +1,19 @@
+TEMPLATE = app
+
+QT += qml quick gamepad
+
+SOURCES += main.cpp
+
+RESOURCES += qml.qrc
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+QML_IMPORT_PATH =
+
+# Default rules for deployment.
+include(deployment.pri)
+
+DISTFILES += \
+ android/AndroidManifest.xml
+
+ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
+
diff --git a/examples/configureButtons/deployment.pri b/examples/configureButtons/deployment.pri
new file mode 100644
index 0000000..c89af25
--- /dev/null
+++ b/examples/configureButtons/deployment.pri
@@ -0,0 +1,14 @@
+unix:!android {
+ isEmpty(target.path) {
+ qnx {
+ target.path = /tmp/$${TARGET}/bin
+ } else {
+ target.path = /opt/$${TARGET}/bin
+ }
+ export(target.path)
+ }
+ INSTALLS += target
+}
+
+export(INSTALLS)
+
diff --git a/examples/configureButtons/main.cpp b/examples/configureButtons/main.cpp
new file mode 100644
index 0000000..4ee0220
--- /dev/null
+++ b/examples/configureButtons/main.cpp
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 BogDan Vatra <bogdan@kde.org>
+**
+** 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 <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
+
diff --git a/examples/configureButtons/main.qml b/examples/configureButtons/main.qml
new file mode 100644
index 0000000..31d1cd7
--- /dev/null
+++ b/examples/configureButtons/main.qml
@@ -0,0 +1,616 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 BogDan Vatra <bogdan@kde.org>
+**
+** 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.0
+import QtGamepad 1.0
+
+
+ApplicationWindow {
+ visible: true
+ title: qsTr("Configure gamepad")
+
+ Component.onCompleted: {
+ if (Qt.platform.os === "android")
+ visibility = Window.Maximized
+ }
+
+ property Button checkedButton: null
+ function pressButton(button)
+ {
+ if (checkedButton !== null && button !== checkedButton)
+ checkedButton.checked = false;
+ checkedButton = button
+ }
+
+ Gamepad {
+ id: gamepad
+ deviceId: GamepadManager.connectedGamepads.length > 0 ? GamepadManager.connectedGamepads[0] : -1
+ onDeviceIdChanged: GamepadManager.setCancelConfigureButton(deviceId, GamepadManager.ButtonStart);
+ }
+
+ Connections {
+ target: GamepadManager
+ onButtonConfigured: pressButton(null)
+ onAxisConfigured: pressButton(null)
+ onConfigurationCanceled: pressButton(null)
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Connected gamepads")
+ }
+ ComboBox {
+ model: GamepadManager.connectedGamepads
+ onCurrentIndexChanged: gamepad.deviceId[GamepadManager.connectedGamepads[currentIndex]]
+ }
+ }
+
+ Text {
+ Layout.fillWidth: true
+ text: qsTr("Start button cancel's current configuration")
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ GroupBox {
+ title: qsTr("Configure Gamepad Buttons")
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ ColumnLayout {
+ anchors.fill: parent
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("ButtonA")
+ horizontalAlignment: Text.AlignRight
+ }
+
+ Text {
+ text: gamepad.buttonA ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonA);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("ButtonB")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonB ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonB);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("ButtonX")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonX ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonX);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("ButtonY")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonY ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonY);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("ButtonStart")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonStart ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonStart);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("ButtonSelect")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonSelect ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonSelect);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button L1")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonL1 ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonL1);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button R1")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonR1 ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonR1);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button L2")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonL2 ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonL2);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button R2")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonR2 ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonR2);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button L3")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonL3 ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonL3);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button R3")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonR3 ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonR3);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button Up")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonUp ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonUp);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button Down")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ text: gamepad.buttonDown ? qsTr("DOWN") : qsTr("UP")
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonDown );
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button Left")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ Layout.fillWidth: true
+ text: gamepad.buttonLeft ? qsTr("DOWN") : qsTr("UP")
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonLeft);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button Right")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ Layout.fillWidth: true
+ text: gamepad.buttonRight ? qsTr("DOWN") : qsTr("UP")
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonRight);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button Center")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ Layout.fillWidth: true
+ text: gamepad.buttonCenter ? qsTr("DOWN") : qsTr("UP")
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonCenter);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("Button Guide")
+ horizontalAlignment: Text.AlignRight
+ }
+ Text {
+ Layout.fillWidth: true
+ text: gamepad.buttonGuide ? qsTr("DOWN") : qsTr("UP")
+ horizontalAlignment: Text.AlignHCenter
+ }
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureButton(gamepad.deviceId, GamepadManager.ButtonGuide);
+ }
+ }
+ }
+ }
+ }
+ GroupBox {
+ title: qsTr("Gamepad Axies")
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+
+ ColumnLayout {
+ anchors.fill: parent
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("AxisLeftX")
+ horizontalAlignment: Text.AlignRight
+ }
+
+ Text {
+ text: gamepad.axisLeftX
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureAxis(gamepad.deviceId, GamepadManager.AxisLeftX);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("AxisLeftY")
+ horizontalAlignment: Text.AlignRight
+ }
+
+ Text {
+ text: gamepad.axisLeftY
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureAxis(gamepad.deviceId, GamepadManager.AxisLeftY);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("AxisRightX")
+ horizontalAlignment: Text.AlignRight
+ }
+
+ Text {
+ text: gamepad.axisRightX
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureAxis(gamepad.deviceId, GamepadManager.AxisRightX);
+ }
+ }
+ }
+ RowLayout {
+ Layout.fillWidth: true
+ Text {
+ text: qsTr("AxisRightY")
+ horizontalAlignment: Text.AlignRight
+ }
+
+ Text {
+ text: gamepad.axisRightY
+ Layout.fillWidth: true
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Button {
+ text: qsTr("Configure")
+ checkable: true
+ enabled: !checked
+ onCheckedChanged: {
+ pressButton(this);
+ if (checked)
+ GamepadManager.configureAxis(gamepad.deviceId, GamepadManager.AxisRightY);
+ }
+ }
+ }
+
+ Item {
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/configureButtons/qml.qrc b/examples/configureButtons/qml.qrc
new file mode 100644
index 0000000..5f6483a
--- /dev/null
+++ b/examples/configureButtons/qml.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>main.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/examples.pro b/examples/examples.pro
index 4461e4d..e5acdd9 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -5,5 +5,6 @@ SUBDIRS += simple \
qtHaveModule(quick) {
SUBDIRS += quickGamepad \
- keyNavigation
+ keyNavigation \
+ configureButtons
}
diff --git a/examples/keyNavigation/qml/main.qml b/examples/keyNavigation/qml/main.qml
index 2c4106c..d58bfc5 100644
--- a/examples/keyNavigation/qml/main.qml
+++ b/examples/keyNavigation/qml/main.qml
@@ -51,7 +51,12 @@ ApplicationWindow {
Gamepad {
id: gamepad1
- index: 0
+ deviceId: GamepadManager.connectedGamepads.length > 0 ? GamepadManager.connectedGamepads[0] : -1
+ }
+
+ Connections {
+ target: GamepadManager
+ onGamepadConnected: gamepad1.deviceId = deviceId
}
GamepadKeyNavigation {
diff --git a/examples/mouseItem/qml/main.qml b/examples/mouseItem/qml/main.qml
index 4ab1f8a..4c05e90 100644
--- a/examples/mouseItem/qml/main.qml
+++ b/examples/mouseItem/qml/main.qml
@@ -49,9 +49,14 @@ ApplicationWindow {
height: 480
title: qsTr("Hello World")
+ Connections {
+ target: GamepadManager
+ onGamepadConnected: gamepad1.deviceId = deviceId
+ }
+
Gamepad {
id: gamepad1
- index: 0
+ deviceId: GamepadManager.connectedGamepads.length > 0 ? GamepadManager.connectedGamepads[0] : -1
onButtonAChanged: {
if (value == true) {
diff --git a/examples/quickGamepad/qml/main.qml b/examples/quickGamepad/qml/main.qml
index 10b6674..049e72b 100644
--- a/examples/quickGamepad/qml/main.qml
+++ b/examples/quickGamepad/qml/main.qml
@@ -226,10 +226,13 @@ ApplicationWindow {
}
-
+ Connections {
+ target: GamepadManager
+ onGamepadConnected: gamepad.deviceId = deviceId
+ }
Gamepad {
id: gamepad
+ deviceId: GamepadManager.connectedGamepads.length > 0 ? GamepadManager.connectedGamepads[0] : -1
}
-
}
diff --git a/src/gamepad/qgamepadbackend.cpp b/src/gamepad/qgamepadbackend.cpp
index 812a940..0f11800 100644
--- a/src/gamepad/qgamepadbackend.cpp
+++ b/src/gamepad/qgamepadbackend.cpp
@@ -35,6 +35,7 @@
****************************************************************************/
#include "qgamepadbackend_p.h"
+#include <QSettings>
QT_BEGIN_NAMESPACE
@@ -43,6 +44,61 @@ QGamepadBackend::QGamepadBackend(QObject *parent) :
{
}
+bool QGamepadBackend::isConfigurationNeeded(int /*deviceId*/)
+{
+ return false;
+}
+
+void QGamepadBackend::resetConfiguration(int /*deviceId*/)
+{
+}
+
+bool QGamepadBackend::configureButton(int /*deviceId*/, QGamepadManager::GamepadButton /*button*/)
+{
+ return false;
+}
+
+bool QGamepadBackend::configureAxis(int /*deviceId*/, QGamepadManager::GamepadAxis /*axis*/)
+{
+ return false;
+}
+
+bool QGamepadBackend::setCancelConfigureButton(int /*deviceId*/, QGamepadManager::GamepadButton /*button*/)
+{
+ return false;
+}
+
+void QGamepadBackend::setSettingsFile(const QString &file)
+{
+ m_settingsFilePath = file;
+}
+
+void QGamepadBackend::saveSettings(int productId, const QVariant &value)
+{
+ QScopedPointer<QSettings> s;
+ if (m_settingsFilePath.isNull())
+ s.reset(new QSettings());
+ else
+ s.reset(new QSettings(m_settingsFilePath));
+ s->beginGroup(QString::fromLatin1("___gamepad_saved_states"));
+ QString key = QString::fromLatin1("id_%1").arg(productId);
+ if (value.isNull())
+ s->remove(key);
+ else
+ s->setValue(key, value);
+}
+
+QVariant QGamepadBackend::readSettings(int productId)
+{
+ QScopedPointer<QSettings> s;
+ if (m_settingsFilePath.isNull())
+ s.reset(new QSettings());
+ else
+ s.reset(new QSettings(m_settingsFilePath));
+ s->beginGroup(QString::fromLatin1("___gamepad_saved_states"));
+ return s->value(QString::fromLatin1("id_%1").arg(productId));
+}
+
bool QGamepadBackend::start()
{
return true;
diff --git a/src/gamepad/qgamepadbackend_p.h b/src/gamepad/qgamepadbackend_p.h
index 60f01dc..e170511 100644
--- a/src/gamepad/qgamepadbackend_p.h
+++ b/src/gamepad/qgamepadbackend_p.h
@@ -48,18 +48,53 @@ class Q_GAMEPAD_EXPORT QGamepadBackend : public QObject
{
Q_OBJECT
public:
+ template <typename T>
+ struct AxisInfo {
+ AxisInfo(T minValue = 0, T maxValue = 1, QGamepadManager::GamepadAxis gamepadAxis = QGamepadManager::AxisInvalid)
+ : minValue(minValue)
+ , maxValue(maxValue)
+ , gamepadAxis(gamepadAxis)
+ {}
+
+ virtual double normalized(T value) const
+ {
+ if (minValue >= 0)
+ return 2.0 * double(value - double(maxValue - minValue) / 2.0) / double(maxValue - minValue);
+ else
+ return 2.0 * double(value - minValue) / double(maxValue - minValue) - 1.0;
+ }
+ T minValue;
+ T maxValue;
+ QGamepadManager::GamepadAxis gamepadAxis;
+ };
+
+public:
explicit QGamepadBackend(QObject *parent = 0);
+ virtual bool isConfigurationNeeded(int deviceId);
+ virtual void resetConfiguration(int deviceId);
+ virtual bool configureButton(int deviceId, QGamepadManager::GamepadButton button);
+ virtual bool configureAxis(int deviceId, QGamepadManager::GamepadAxis axis);
+ virtual bool setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button);
+ virtual void setSettingsFile(const QString &file);
+ virtual void saveSettings(int productId, const QVariant &value);
+ virtual QVariant readSettings(int productId);
public slots:
virtual bool start();
virtual void stop();
signals:
+ void buttonConfigured(int deviceId, QGamepadManager::GamepadButton button);
+ void axisConfigured(int deviceId, QGamepadManager::GamepadAxis axis);
+ void configurationCanceled(int deviceId);
void gamepadAdded(int deviceId);
void gamepadRemoved(int deviceId);
void gamepadAxisMoved(int deviceId, QGamepadManager::GamepadAxis axis, double value);
void gamepadButtonPressed(int deviceId, QGamepadManager::GamepadButton button, double value);
void gamepadButtonReleased(int deviceId, QGamepadManager::GamepadButton button);
+
+protected:
+ QString m_settingsFilePath;
};
QT_END_NAMESPACE
diff --git a/src/gamepad/qgamepadmanager.cpp b/src/gamepad/qgamepadmanager.cpp
index 91ca70f..2c737f6 100644
--- a/src/gamepad/qgamepadmanager.cpp
+++ b/src/gamepad/qgamepadmanager.cpp
@@ -58,6 +58,9 @@ QGamepadManager::QGamepadManager() :
connect(m_gamepadBackend, SIGNAL(gamepadAxisMoved(int,QGamepadManager::GamepadAxis,double)), this, SLOT(forwardGamepadAxisEvent(int,QGamepadManager::GamepadAxis,double)));
connect(m_gamepadBackend, SIGNAL(gamepadButtonPressed(int,QGamepadManager::GamepadButton,double)), this, SLOT(forwardGamepadButtonPressEvent(int,QGamepadManager::GamepadButton,double)));
connect(m_gamepadBackend, SIGNAL(gamepadButtonReleased(int,QGamepadManager::GamepadButton)), this, SLOT(forwardGamepadButtonReleaseEvent(int,QGamepadManager::GamepadButton)));
+ connect(m_gamepadBackend, &QGamepadBackend::buttonConfigured, this, &QGamepadManager::buttonConfigured);
+ connect(m_gamepadBackend, &QGamepadBackend::axisConfigured, this, &QGamepadManager::axisConfigured);
+ connect(m_gamepadBackend, &QGamepadBackend::configurationCanceled, this, &QGamepadManager::configurationCanceled);
if (!m_gamepadBackend->start())
qCWarning(gp) << "Failed to start gamepad backend";
@@ -107,6 +110,36 @@ const QList<int> QGamepadManager::connectedGamepads() const
return m_connectedGamepads.toList();
}
+bool QGamepadManager::isConfigurationNeeded(int deviceId)
+{
+ return m_gamepadBackend->isConfigurationNeeded(deviceId);
+}
+
+bool QGamepadManager::configureButton(int deviceId, QGamepadManager::GamepadButton button)
+{
+ return m_gamepadBackend->configureButton(deviceId, button);
+}
+
+bool QGamepadManager::configureAxis(int deviceId, QGamepadManager::GamepadAxis axis)
+{
+ return m_gamepadBackend->configureAxis(deviceId, axis);
+}
+
+bool QGamepadManager::setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button)
+{
+ return m_gamepadBackend->setCancelConfigureButton(deviceId, button);
+}
+
+void QGamepadManager::resetConfiguration(int deviceId)
+{
+ m_gamepadBackend->resetConfiguration(deviceId);
+}
+
+void QGamepadManager::setSettingsFile(const QString &file)
+{
+ m_gamepadBackend->setSettingsFile(file);
+}
+
void QGamepadManager::forwardGamepadConnected(int deviceId)
{
//qDebug() << "gamepad connected: " << index;
diff --git a/src/gamepad/qgamepadmanager.h b/src/gamepad/qgamepadmanager.h
index 6502ff6..4d52191 100644
--- a/src/gamepad/qgamepadmanager.h
+++ b/src/gamepad/qgamepadmanager.h
@@ -90,6 +90,14 @@ public:
bool isGamepadConnected(int deviceId);
const QList<int> connectedGamepads() const;
+public Q_SLOTS:
+ bool isConfigurationNeeded(int deviceId);
+ bool configureButton(int deviceId, GamepadButton button);
+ bool configureAxis(int deviceId, GamepadAxis axis);
+ bool setCancelConfigureButton(int deviceId, GamepadButton button);
+ void resetConfiguration(int deviceId);
+ void setSettingsFile(const QString &file);
+
Q_SIGNALS:
void connectedGamepadsChanged();
void gamepadConnected(int deviceId);
@@ -97,6 +105,9 @@ Q_SIGNALS:
void gamepadAxisEvent(int deviceId, QGamepadManager::GamepadAxis axis, double value);
void gamepadButtonPressEvent(int deviceId, QGamepadManager::GamepadButton button, double value);
void gamepadButtonReleaseEvent(int deviceId, QGamepadManager::GamepadButton button);
+ void buttonConfigured(int deviceId, QGamepadManager::GamepadButton button);
+ void axisConfigured(int deviceId, QGamepadManager::GamepadAxis axis);
+ void configurationCanceled(int deviceId);
private Q_SLOTS:
void forwardGamepadConnected(int deviceId);
diff --git a/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp b/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp
index 6319441..17bd74e 100644
--- a/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp
+++ b/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp
@@ -45,8 +45,59 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcEGB, "qt.gamepad")
+QEvdevGamepadDevice::EvdevAxisInfo::EvdevAxisInfo()
+ : QGamepadBackend::AxisInfo<int>(0, 1, QGamepadManager::AxisInvalid)
+{
+}
+
+QEvdevGamepadDevice::EvdevAxisInfo::EvdevAxisInfo(int fd, quint16 abs, int min, int max, QGamepadManager::GamepadAxis gamepadAxis)
+ : QGamepadBackend::AxisInfo<int>(min, max, gamepadAxis)
+ , flat(0)
+ , gamepadMinButton(QGamepadManager::ButtonInvalid)
+ , gamepadMaxButton(QGamepadManager::ButtonInvalid)
+ , gamepadLastButton(QGamepadManager::ButtonInvalid)
+{
+ setAbsInfo(fd, abs);
+}
+
+double QEvdevGamepadDevice::EvdevAxisInfo::normalized(int value) const
+{
+ double val = AxisInfo::normalized(value);
+ if (qAbs(val) <= flat)
+ val = 0;
+ return val;
+}
+
+void QEvdevGamepadDevice::EvdevAxisInfo::setAbsInfo(int fd, int abs)
+{
+ input_absinfo absInfo;
+ memset(&absInfo, 0, sizeof(input_absinfo));
+ if (ioctl(fd, EVIOCGABS(abs), &absInfo) >= 0) {
+ minValue = absInfo.minimum;
+ maxValue = absInfo.maximum;
+ if (maxValue - minValue)
+ flat = fabs(absInfo.flat / double(maxValue - minValue));
+ }
+}
+
+void QEvdevGamepadDevice::EvdevAxisInfo::restoreSavedData(int fd, int abs, const QVariantMap &value)
+{
+ gamepadAxis = QGamepadManager::GamepadAxis(value[QLatin1Literal("axis")].toInt());
+ gamepadMinButton = QGamepadManager::GamepadButton(value[QLatin1Literal("minButton")].toInt());
+ gamepadMaxButton = QGamepadManager::GamepadButton(value[QLatin1Literal("maxButton")].toInt());
+ setAbsInfo(fd, abs);
+}
+
+QVariantMap QEvdevGamepadDevice::EvdevAxisInfo::dataToSave() const
+{
+ QVariantMap data;
+ data[QLatin1Literal("axis")] = gamepadAxis;
+ data[QLatin1Literal("minButton")] = gamepadMinButton;
+ data[QLatin1Literal("maxButton")] = gamepadMaxButton;
+ return data;
+}
+
QEvdevGamepadBackend::QEvdevGamepadBackend()
- : m_nextIndex(0)
{
}
@@ -83,17 +134,46 @@ QEvdevGamepadDevice *QEvdevGamepadBackend::newDevice(const QByteArray &device)
return new QEvdevGamepadDevice(device, this);
}
-// To be called only when it is sure that there is a controller on-line.
-int QEvdevGamepadBackend::idForDevice(const QByteArray &device)
+QEvdevGamepadDevice *QEvdevGamepadBackend::device(int deviceId)
{
- int index;
- if (m_devIndex.contains(device)) {
- index = m_devIndex[device];
- } else {
- index = m_nextIndex++;
- m_devIndex[device] = index;
- }
- return index;
+ foreach (QEvdevGamepadDevice *device, m_devices)
+ if (device->deviceId() == deviceId)
+ return device;
+ return nullptr;
+}
+
+void QEvdevGamepadBackend::resetConfiguration(int deviceId)
+{
+ if (QEvdevGamepadDevice *dev = device(deviceId))
+ return dev->resetConfiguration();
+}
+
+bool QEvdevGamepadBackend::isConfigurationNeeded(int deviceId)
+{
+ if (QEvdevGamepadDevice *dev = device(deviceId))
+ return dev->isConfigurationNeeded();
+ return false;
+}
+
+bool QEvdevGamepadBackend::configureButton(int deviceId, QGamepadManager::GamepadButton button)
+{
+ if (QEvdevGamepadDevice *dev = device(deviceId))
+ return dev->configureButton(button);
+ return false;
+}
+
+bool QEvdevGamepadBackend::configureAxis(int deviceId, QGamepadManager::GamepadAxis axis)
+{
+ if (QEvdevGamepadDevice *dev = device(deviceId))
+ return dev->configureAxis(axis);
+ return false;
+}
+
+bool QEvdevGamepadBackend::setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button)
+{
+ if (QEvdevGamepadDevice *dev = device(deviceId))
+ return dev->setCancelConfigureButton(button);
+ return false;
}
void QEvdevGamepadBackend::stop()
@@ -128,11 +208,12 @@ void QEvdevGamepadBackend::handleRemovedDevice(const QString &device)
QEvdevGamepadDevice::QEvdevGamepadDevice(const QByteArray &dev, QEvdevGamepadBackend *backend)
: m_dev(dev),
m_backend(backend),
- m_index(-1),
m_fd(-1),
+ m_productId(0),
+ m_needsConfigure(true),
m_notifier(0),
- m_prevYHatButton(QGamepadManager::ButtonInvalid),
- m_prevXHatButton(QGamepadManager::ButtonInvalid)
+ m_configureButton(QGamepadManager::ButtonInvalid),
+ m_configureAxis(QGamepadManager::AxisInvalid)
{
openDevice(dev);
}
@@ -141,8 +222,75 @@ QEvdevGamepadDevice::~QEvdevGamepadDevice()
{
if (m_fd != -1)
QT_CLOSE(m_fd);
- if (m_index >= 0)
- emit m_backend->gamepadRemoved(m_index);
+ if (m_fd >= 0)
+ emit m_backend->gamepadRemoved(m_fd);
+}
+
+void QEvdevGamepadDevice::resetConfiguration()
+{
+ m_axisMap.insert(ABS_X, EvdevAxisInfo(m_fd, ABS_X, -32768, 32767, QGamepadManager::AxisLeftX));
+ m_axisMap.insert(ABS_Y, EvdevAxisInfo(m_fd, ABS_Y, -32768, 32767, QGamepadManager::AxisLeftY));
+ m_axisMap.insert(ABS_RX, EvdevAxisInfo(m_fd, ABS_RX, -32768, 32767, QGamepadManager::AxisRightX));
+ m_axisMap.insert(ABS_RY, EvdevAxisInfo(m_fd, ABS_RY, -32768, 32767, QGamepadManager::AxisRightY));
+ m_axisMap.insert(ABS_Z, EvdevAxisInfo(m_fd, ABS_Z, 0, 255));
+ m_axisMap[ABS_Z].gamepadMinButton = QGamepadManager::ButtonL2;
+ m_axisMap[ABS_Z].gamepadMaxButton = QGamepadManager::ButtonL2;
+
+ m_axisMap.insert(ABS_RZ, EvdevAxisInfo(m_fd, ABS_RZ, 0, 255));
+ m_axisMap[ABS_RZ].gamepadMinButton = QGamepadManager::ButtonR2;
+ m_axisMap[ABS_RZ].gamepadMaxButton = QGamepadManager::ButtonR2;
+
+ m_axisMap.insert(ABS_HAT0X, EvdevAxisInfo(m_fd, ABS_HAT0X, -1, 1));
+ m_axisMap[ABS_HAT0X].gamepadMinButton = QGamepadManager::ButtonLeft;
+ m_axisMap[ABS_HAT0X].gamepadMaxButton = QGamepadManager::ButtonRight;
+
+ m_axisMap.insert(ABS_HAT0Y, EvdevAxisInfo(m_fd, ABS_HAT0Y, -1, 1));
+ m_axisMap[ABS_HAT0Y].gamepadMinButton = QGamepadManager::ButtonUp;
+ m_axisMap[ABS_HAT0Y].gamepadMaxButton = QGamepadManager::ButtonDown;
+
+ m_buttonsMap[BTN_START] = QGamepadManager::ButtonStart;
+ m_buttonsMap[BTN_SELECT] = QGamepadManager::ButtonSelect;
+ m_buttonsMap[BTN_MODE] = QGamepadManager::ButtonGuide;
+ m_buttonsMap[BTN_X] = QGamepadManager::ButtonX;
+ m_buttonsMap[BTN_Y] = QGamepadManager::ButtonY;
+ m_buttonsMap[BTN_A] = QGamepadManager::ButtonA;
+ m_buttonsMap[BTN_B] = QGamepadManager::ButtonB;
+ m_buttonsMap[BTN_TL] = QGamepadManager::ButtonL1;
+ m_buttonsMap[BTN_TR] = QGamepadManager::ButtonR1;
+ m_buttonsMap[BTN_TL2] = QGamepadManager::ButtonL2;
+ m_buttonsMap[BTN_TR2] = QGamepadManager::ButtonR2;
+ m_buttonsMap[BTN_THUMB] = m_buttonsMap[BTN_THUMBL] = QGamepadManager::ButtonL3;
+ m_buttonsMap[BTN_THUMBR] = QGamepadManager::ButtonR3;
+ m_buttonsMap[BTN_TRIGGER_HAPPY1] = QGamepadManager::ButtonLeft;
+ m_buttonsMap[BTN_TRIGGER_HAPPY2] = QGamepadManager::ButtonRight;
+ m_buttonsMap[BTN_TRIGGER_HAPPY3] = QGamepadManager::ButtonUp;
+ m_buttonsMap[BTN_TRIGGER_HAPPY4] = QGamepadManager::ButtonDown;
+
+ if (m_productId)
+ m_backend->saveSettings(m_productId, QVariant());
+}
+
+bool QEvdevGamepadDevice::isConfigurationNeeded()
+{
+ return m_needsConfigure;
+}
+
+bool QEvdevGamepadDevice::configureButton(QGamepadManager::GamepadButton button)
+{
+ m_configureButton = button;
+ return true;
+}
+
+bool QEvdevGamepadDevice::configureAxis(QGamepadManager::GamepadAxis axis)
+{
+ m_configureAxis = axis;
+ return true;
+}
+
+bool QEvdevGamepadDevice::setCancelConfigureButton(QGamepadManager::GamepadButton button)
+{
+ m_configureCancelButton = button;
+ return true;
}
bool QEvdevGamepadDevice::openDevice(const QByteArray &dev)
@@ -152,41 +300,41 @@ bool QEvdevGamepadDevice::openDevice(const QByteArray &dev)
if (m_fd >= 0) {
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readData()));
+ emit m_backend->gamepadAdded(m_fd);
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, 0));
- m_axisInfo.insert(ABS_Y, AxisInfo(-32768, 32767, 0));
- m_axisInfo.insert(ABS_RX, AxisInfo(-32768, 32767, 0));
- m_axisInfo.insert(ABS_RY, AxisInfo(-32768, 32767, 0));
- m_axisInfo.insert(ABS_Z, AxisInfo(0, 255, 0));
- m_axisInfo.insert(ABS_RZ, AxisInfo(0, 255, 0));
+ input_id id;
+ if (ioctl(m_fd, EVIOCGID, &id) >= 0)
+ m_productId = id.product;
+
+ if (m_productId) {
+ QVariant settings = m_backend->readSettings(m_productId);
+ if (!settings.isNull()) {
+ m_needsConfigure = false;
+ QVariantMap data = settings.toMap()[QLatin1Literal("axes")].toMap();
+ for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it) {
+ const int key = it.key().toInt();
+ m_axisMap[key].restoreSavedData(m_fd, key, it.value().toMap());
+ }
- 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, absInfo.flat));
- if (ioctl(m_fd, EVIOCGABS(ABS_Y), &absInfo) >= 0)
- m_axisInfo.insert(ABS_Y, AxisInfo(absInfo.minimum, absInfo.maximum, absInfo.flat));
- if (ioctl(m_fd, EVIOCGABS(ABS_RX), &absInfo) >= 0)
- m_axisInfo.insert(ABS_RX, AxisInfo(absInfo.minimum, absInfo.maximum, absInfo.flat));
- if (ioctl(m_fd, EVIOCGABS(ABS_RY), &absInfo) >= 0)
- m_axisInfo.insert(ABS_RY, AxisInfo(absInfo.minimum, absInfo.maximum, absInfo.flat));
- if (ioctl(m_fd, EVIOCGABS(ABS_Z), &absInfo) >= 0)
- m_axisInfo.insert(ABS_Z, AxisInfo(absInfo.minimum, absInfo.maximum, absInfo.flat));
- if (ioctl(m_fd, EVIOCGABS(ABS_RZ), &absInfo) >= 0)
- m_axisInfo.insert(ABS_RZ, AxisInfo(absInfo.minimum, absInfo.maximum, absInfo.flat));
-
- qCDebug(lcEGB) << "Axis limits:" << m_axisInfo;
+ data = settings.toMap()[QLatin1Literal("buttons")].toMap();
+ for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
+ m_buttonsMap[it.key().toInt()] = QGamepadManager::GamepadButton(it.value().toInt());
+ }
+ }
+ if (m_needsConfigure)
+ resetConfiguration();
+
+ qCDebug(lcEGB) << "Axis limits:" << m_axisMap;
return true;
}
-QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::AxisInfo &axisInfo)
+QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::EvdevAxisInfo &axisInfo)
{
dbg.nospace() << "AxisInfo(min=" << axisInfo.minValue << ", max=" << axisInfo.maxValue << ")";
return dbg.space();
@@ -229,185 +377,135 @@ err:
}
}
-double QEvdevGamepadDevice::AxisInfo::normalized(int value) const
+void QEvdevGamepadDevice::saveData()
{
- double ret = 0;
- if (minValue >= 0)
- ret = 2 * (value - double(maxValue - minValue) / 2) / double(maxValue - minValue);
- else
- ret = 2 * (value - minValue) / double(maxValue - minValue) - 1.0;
- if (fabs(ret) <= fabs(flat))
- ret = 0;
- return ret;
+ if (!m_productId)
+ return ;
+
+ QVariantMap settings, data;
+ for (AxisMap::const_iterator it = m_axisMap.begin(); it != m_axisMap.end(); ++it)
+ data[QString::number(it.key())] = it.value().dataToSave();
+ settings[QLatin1Literal("axes")] = data;
+
+ data.clear();
+ for (ButtonsMap::const_iterator it = m_buttonsMap.begin(); it != m_buttonsMap.end(); ++it)
+ data[QString::number(it.key())] = it.value();
+
+ settings[QLatin1Literal("buttons")] = data;
+
+ m_backend->saveSettings(m_productId, settings);
}
void QEvdevGamepadDevice::processInputEvent(input_event *e)
{
- if (m_index < 0) {
- m_index = m_backend->idForDevice(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;
+ ButtonsMap::const_iterator it = m_buttonsMap.find(e->code);
+ if (it != m_buttonsMap.end())
+ btn = it.value();
- 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_TL2:
- btn = QGamepadManager::ButtonL2;
- break;
- case BTN_TR2:
- btn = QGamepadManager::ButtonR2;
- break;
-
+ const bool pressed = e->value;
+ if (m_configureCancelButton != QGamepadManager::ButtonInvalid &&
+ m_configureCancelButton != m_configureButton &&
+ !pressed && btn == m_configureCancelButton &&
+ (m_configureButton != QGamepadManager::ButtonInvalid ||
+ m_configureAxis != QGamepadManager::AxisInvalid)) {
+ m_configureButton = QGamepadManager::ButtonInvalid;
+ m_configureAxis = QGamepadManager::AxisInvalid;
+ emit m_backend->configurationCanceled(m_fd);
+ return;
+ }
- case BTN_THUMB: // wireless
- case BTN_THUMBL: // wired
- btn = QGamepadManager::ButtonL3;
- break;
- case BTN_THUMBR:
- btn = QGamepadManager::ButtonR3;
- break;
+ if (!pressed && m_configureButton != QGamepadManager::ButtonInvalid) {
+ m_buttonsMap[e->code] = m_configureButton;
+ QGamepadManager::GamepadButton but = m_configureButton;
+ m_configureButton = QGamepadManager::ButtonInvalid;
+ saveData();
+ emit m_backend->buttonConfigured(m_fd, but);
+ }
- // 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;
+ it = m_buttonsMap.find(e->code);
+ if (it != m_buttonsMap.end())
+ btn = it.value();
- default:
- break;
- }
if (btn != QGamepadManager::ButtonInvalid) {
if (pressed)
- emit m_backend->gamepadButtonPressed(m_index, btn, 1.0);
+ emit m_backend->gamepadButtonPressed(m_fd, btn, 1.0);
else
- emit m_backend->gamepadButtonReleased(m_index, btn);
+ emit m_backend->gamepadButtonReleased(m_fd, 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);
+ if (m_configureAxis != QGamepadManager::AxisInvalid) {
+ m_axisMap.insert(e->code, EvdevAxisInfo(m_fd, e->code, -32768, 32767, m_configureAxis));
+
+ QGamepadManager::GamepadAxis axis = m_configureAxis;
+ m_configureAxis = QGamepadManager::AxisInvalid;
+
+ saveData();
+ emit m_backend->axisConfigured(m_fd, axis);
+ }
+
+ AxisMap::iterator it = m_axisMap.find(e->code);
+ if (m_configureButton != QGamepadManager::ButtonInvalid) {
+ EvdevAxisInfo axisInfo;
+ if (it != m_axisMap.end())
+ axisInfo = it.value();
+ else
+ axisInfo = EvdevAxisInfo(m_fd, e->code);
+
+ axisInfo.gamepadAxis = QGamepadManager::AxisInvalid;
+
+ bool save = false;
+ if (e->value == axisInfo.minValue) {
+ axisInfo.gamepadMinButton = m_configureButton;
+ if (axisInfo.gamepadMaxButton != QGamepadManager::ButtonInvalid)
+ axisInfo.gamepadMaxButton = m_configureButton;
+ save = true;
+ } else if (e->value == axisInfo.maxValue) {
+ axisInfo.gamepadMaxButton = m_configureButton;
+ if (axisInfo.gamepadMinButton != QGamepadManager::ButtonInvalid)
+ axisInfo.gamepadMinButton = m_configureButton;
+ save = true;
}
- } 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;
+ if (save) {
+ QGamepadManager::GamepadButton but = m_configureButton;
+ m_configureButton = QGamepadManager::ButtonInvalid;
+ saveData();
+ emit m_backend->buttonConfigured(m_fd, but);
}
+ }
+
+ it = m_axisMap.find(e->code);
+ if (it == m_axisMap.end())
+ return;
+
+ EvdevAxisInfo &info = it.value();
- AxisInfo axisInfo = m_axisInfo.value(e->code);
- const double value = axisInfo.normalized(e->value);
+ double val = info.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);
+ if (info.gamepadAxis != QGamepadManager::AxisInvalid)
+ emit m_backend->gamepadAxisMoved(m_fd, info.gamepadAxis, val);
+
+ if (info.gamepadMaxButton == info.gamepadMinButton &&
+ info.gamepadMaxButton != QGamepadManager::ButtonInvalid) {
+ if (val)
+ emit m_backend->gamepadButtonPressed(m_fd, info.gamepadMaxButton, val);
+ else
+ emit m_backend->gamepadButtonReleased(m_fd, info.gamepadMaxButton);
+ } else {
+ if (info.gamepadMaxButton != QGamepadManager::ButtonInvalid
+ && val == 1.0) {
+ info.gamepadLastButton = info.gamepadMaxButton;
+ emit m_backend->gamepadButtonPressed(m_fd, info.gamepadMaxButton, val);
+ } else if (info.gamepadMinButton != QGamepadManager::ButtonInvalid
+ && val == -1.0) {
+ info.gamepadLastButton = info.gamepadMinButton;
+ emit m_backend->gamepadButtonPressed(m_fd, info.gamepadMinButton, val);
+ } else if (!val && info.gamepadLastButton != QGamepadManager::ButtonInvalid) {
+ QGamepadManager::GamepadButton but = info.gamepadLastButton;
+ info.gamepadLastButton = QGamepadManager::ButtonInvalid;
+ emit m_backend->gamepadButtonReleased(m_fd, but);
}
}
}
diff --git a/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h b/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h
index e9e587c..bd7d387 100644
--- a/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h
+++ b/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h
@@ -58,35 +58,54 @@ public:
QEvdevGamepadDevice(const QByteArray &dev, QEvdevGamepadBackend *backend);
~QEvdevGamepadDevice();
QByteArray deviceName() const { return m_dev; }
+ int deviceId() const { return m_fd; }
+ void resetConfiguration();
+ bool isConfigurationNeeded();
+ bool configureButton(QGamepadManager::GamepadButton button);
+ bool configureAxis(QGamepadManager::GamepadAxis axis);
+ bool setCancelConfigureButton(QGamepadManager::GamepadButton button);
private slots:
void readData();
private:
+ void saveData();
void processInputEvent(input_event *e);
bool openDevice(const QByteArray &dev);
QByteArray m_dev;
QEvdevGamepadBackend *m_backend;
- int m_index;
int m_fd;
+ int m_productId;
+ bool m_needsConfigure;
QSocketNotifier *m_notifier;
- struct AxisInfo {
- AxisInfo() : minValue(0), maxValue(1), flat(0) { }
- AxisInfo(int minValue, int maxValue, int flat) : minValue(minValue), maxValue(maxValue), flat(fabs((maxValue-minValue) ? flat/double(maxValue-minValue) : 0)) { }
+ struct EvdevAxisInfo : public QGamepadBackend::AxisInfo<int>
+ {
+ EvdevAxisInfo();
+ EvdevAxisInfo(int fd, quint16 abs, int minValue = 0, int maxValue = 1, QGamepadManager::GamepadAxis gamepadAxis = QGamepadManager::AxisInvalid);
double normalized(int value) const;
-
- int minValue;
- int maxValue;
+ void setAbsInfo(int fd, int abs);
+ void restoreSavedData(int fd, int abs, const QVariantMap &value);
+ QVariantMap dataToSave() const;
double flat;
+ QGamepadManager::GamepadButton gamepadMinButton;
+ QGamepadManager::GamepadButton gamepadMaxButton;
+ QGamepadManager::GamepadButton gamepadLastButton;
};
- QHash<int, AxisInfo> m_axisInfo;
- QGamepadManager::GamepadButton m_prevYHatButton;
- QGamepadManager::GamepadButton m_prevXHatButton;
- friend QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::AxisInfo &axisInfo);
+ typedef QHash<int, EvdevAxisInfo> AxisMap;
+ AxisMap m_axisMap;
+
+ friend QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::EvdevAxisInfo &axisInfo);
+
+ typedef QHash<int, QGamepadManager::GamepadButton> ButtonsMap;
+ ButtonsMap m_buttonsMap;
+
+ QGamepadManager::GamepadButton m_configureButton;
+ QGamepadManager::GamepadAxis m_configureAxis;
+ QGamepadManager::GamepadButton m_configureCancelButton;
};
-QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::AxisInfo &axisInfo);
+QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::EvdevAxisInfo &axisInfo);
class QEvdevGamepadBackend : public QGamepadBackend
{
@@ -96,7 +115,11 @@ public:
QEvdevGamepadBackend();
bool start() Q_DECL_OVERRIDE;
void stop() Q_DECL_OVERRIDE;
- int idForDevice(const QByteArray &device);
+ void resetConfiguration(int deviceId) Q_DECL_OVERRIDE;
+ bool isConfigurationNeeded(int deviceId) Q_DECL_OVERRIDE;
+ bool configureButton(int deviceId, QGamepadManager::GamepadButton button) Q_DECL_OVERRIDE;
+ bool configureAxis(int deviceId, QGamepadManager::GamepadAxis axis) Q_DECL_OVERRIDE;
+ bool setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button) Q_DECL_OVERRIDE;
private slots:
void handleAddedDevice(const QString &device);
@@ -104,11 +127,10 @@ private slots:
private:
QEvdevGamepadDevice *newDevice(const QByteArray &device);
+ QEvdevGamepadDevice *device(int deviceId);
QDeviceDiscovery *m_discovery;
QVector<QEvdevGamepadDevice *> m_devices;
- QHash<QByteArray, int> m_devIndex;
- int m_nextIndex;
};
QT_END_NAMESPACE