aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorJohan Solbakken <johan.hvaler@gmail.com>2023-08-15 12:25:56 +0200
committerAndy Nichols <nezticle@gmail.com>2024-06-09 19:22:21 +0200
commit39bbb8049f5d56eaf611e6dfa9db64cd70616137 (patch)
treebc50e1e24d983f6c369a82e3a7012dc6dcefe1b4 /src/plugins
parent1712e01fb1564705338532ae14a77acda8acd5cb (diff)
Expand scope of QtGamepad to QtUniversalInputHEADdev
This patch transforms the QtGamepad module from Qt 5 into a new module that expands the scope to include all forms of exotic input. The majority of the previous QtGamepad functionality and APIs are preserved, but now there is also a more general API for getting the state of input devices like joysticks, flight yokes, and any other devices that define themselves in terms of buttons and axes. In addition to being able to receive input, it is also now possible to output feedback in devices that support it. Experimental input mapping functionality has been added to facilitate mapping an arbitrary input to a predefined action. Change-Id: Ic35b73282944cfd4eea9defbcf82c4abc55d7237 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/CMakeLists.txt5
-rw-r--r--src/plugins/gamepads/android/android.pro2
-rw-r--r--src/plugins/gamepads/android/jar/jar.pro18
-rw-r--r--src/plugins/gamepads/android/jar/src/org/qtproject/qt/android/gamepad/QtGamepad.java110
-rw-r--r--src/plugins/gamepads/android/src/main.cpp61
-rw-r--r--src/plugins/gamepads/android/src/qandroidgamepadbackend.cpp635
-rw-r--r--src/plugins/gamepads/android/src/qandroidgamepadbackend_p.h135
-rw-r--r--src/plugins/gamepads/android/src/src.pro17
-rw-r--r--src/plugins/gamepads/darwin/darwin.json3
-rw-r--r--src/plugins/gamepads/darwin/darwin.pro19
-rw-r--r--src/plugins/gamepads/darwin/main.cpp61
-rw-r--r--src/plugins/gamepads/darwin/qdarwingamepadbackend.mm570
-rw-r--r--src/plugins/gamepads/darwin/qdarwingamepadbackend_p.h77
-rw-r--r--src/plugins/gamepads/evdev/evdev.json3
-rw-r--r--src/plugins/gamepads/evdev/evdev.pro14
-rw-r--r--src/plugins/gamepads/evdev/main.cpp62
-rw-r--r--src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp553
-rw-r--r--src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h138
-rw-r--r--src/plugins/gamepads/gamepads.pro7
-rw-r--r--src/plugins/gamepads/sdl2/main.cpp61
-rw-r--r--src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp210
-rw-r--r--src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h74
-rw-r--r--src/plugins/gamepads/sdl2/sdl2.json3
-rw-r--r--src/plugins/gamepads/sdl2/sdl2.pro17
-rw-r--r--src/plugins/gamepads/xinput/main.cpp62
-rw-r--r--src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp291
-rw-r--r--src/plugins/gamepads/xinput/qxinputgamepadbackend_p.h63
-rw-r--r--src/plugins/gamepads/xinput/xinput.json3
-rw-r--r--src/plugins/gamepads/xinput/xinput.pro14
-rw-r--r--src/plugins/joystickinputs/CMakeLists.txt23
-rw-r--r--src/plugins/joystickinputs/android/CMakeLists.txt43
-rw-r--r--src/plugins/joystickinputs/android/android.json (renamed from src/plugins/gamepads/android/src/android.json)0
-rw-r--r--src/plugins/joystickinputs/android/androidjoystickinput.cpp142
-rw-r--r--src/plugins/joystickinputs/android/androidjoystickinput.h39
-rw-r--r--src/plugins/joystickinputs/android/androidjoystickinputplugin.cpp17
-rw-r--r--src/plugins/joystickinputs/android/androidjoystickinputplugin.h23
-rw-r--r--src/plugins/joystickinputs/android/jar/AndroidManifest.xml6
-rw-r--r--src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java28
-rw-r--r--src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystickInputHandler.java308
-rw-r--r--src/plugins/joystickinputs/ios/CMakeLists.txt17
-rw-r--r--src/plugins/joystickinputs/ios/ios.json3
-rw-r--r--src/plugins/joystickinputs/ios/iosjoystickinput.h27
-rw-r--r--src/plugins/joystickinputs/ios/iosjoystickinput.mm318
-rw-r--r--src/plugins/joystickinputs/ios/iosjoystickinputplugin.cpp17
-rw-r--r--src/plugins/joystickinputs/ios/iosjoystickinputplugin.h23
-rw-r--r--src/plugins/joystickinputs/linux/CMakeLists.txt17
-rw-r--r--src/plugins/joystickinputs/linux/linux.json3
-rw-r--r--src/plugins/joystickinputs/linux/linuxjoystickinput.cpp453
-rw-r--r--src/plugins/joystickinputs/linux/linuxjoystickinput.h101
-rw-r--r--src/plugins/joystickinputs/linux/linuxjoystickinputplugin.cpp17
-rw-r--r--src/plugins/joystickinputs/linux/linuxjoystickinputplugin.h23
-rw-r--r--src/plugins/joystickinputs/macos/CMakeLists.txt19
-rw-r--r--src/plugins/joystickinputs/macos/macos.json3
-rw-r--r--src/plugins/joystickinputs/macos/macosjoystickinput.h104
-rw-r--r--src/plugins/joystickinputs/macos/macosjoystickinput.mm623
-rw-r--r--src/plugins/joystickinputs/macos/macosjoystickinputplugin.cpp17
-rw-r--r--src/plugins/joystickinputs/macos/macosjoystickinputplugin.h23
-rw-r--r--src/plugins/joystickinputs/windows/CMakeLists.txt16
-rw-r--r--src/plugins/joystickinputs/windows/windows.json3
-rw-r--r--src/plugins/joystickinputs/windows/windowsjoystickinput.cpp571
-rw-r--r--src/plugins/joystickinputs/windows/windowsjoystickinput.h123
-rw-r--r--src/plugins/joystickinputs/windows/windowsjoystickinputplugin.cpp17
-rw-r--r--src/plugins/joystickinputs/windows/windowsjoystickinputplugin.h23
-rw-r--r--src/plugins/mouseinputs/CMakeLists.txt23
-rw-r--r--src/plugins/mouseinputs/linux/CMakeLists.txt15
-rw-r--r--src/plugins/mouseinputs/linux/linux.json3
-rw-r--r--src/plugins/mouseinputs/linux/linuxmouseinput.cpp84
-rw-r--r--src/plugins/mouseinputs/linux/linuxmouseinput.h41
-rw-r--r--src/plugins/mouseinputs/linux/linuxmouseinputplugin.cpp17
-rw-r--r--src/plugins/mouseinputs/linux/linuxmouseinputplugin.h23
-rw-r--r--src/plugins/mouseinputs/macos/CMakeLists.txt18
-rw-r--r--src/plugins/mouseinputs/macos/macos.json3
-rw-r--r--src/plugins/mouseinputs/macos/macosmouseinput.h41
-rw-r--r--src/plugins/mouseinputs/macos/macosmouseinput.mm82
-rw-r--r--src/plugins/mouseinputs/macos/macosmouseinputplugin.cpp17
-rw-r--r--src/plugins/mouseinputs/macos/macosmouseinputplugin.h23
-rw-r--r--src/plugins/mouseinputs/windows/CMakeLists.txt15
-rw-r--r--src/plugins/mouseinputs/windows/windows.json3
-rw-r--r--src/plugins/mouseinputs/windows/windowsmouseinput.cpp84
-rw-r--r--src/plugins/mouseinputs/windows/windowsmouseinput.h41
-rw-r--r--src/plugins/mouseinputs/windows/windowsmouseinputplugin.cpp17
-rw-r--r--src/plugins/mouseinputs/windows/windowsmouseinputplugin.h23
-rw-r--r--src/plugins/plugins.pro2
83 files changed, 3768 insertions, 3285 deletions
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
new file mode 100644
index 0000000..d4856bd
--- /dev/null
+++ b/src/plugins/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(joystickinputs)
+add_subdirectory(mouseinputs)
diff --git a/src/plugins/gamepads/android/android.pro b/src/plugins/gamepads/android/android.pro
deleted file mode 100644
index 6ad7e3b..0000000
--- a/src/plugins/gamepads/android/android.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-TEMPLATE=subdirs
-SUBDIRS += jar src
diff --git a/src/plugins/gamepads/android/jar/jar.pro b/src/plugins/gamepads/android/jar/jar.pro
deleted file mode 100644
index 879bbd9..0000000
--- a/src/plugins/gamepads/android/jar/jar.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TARGET = Qt$${QT_MAJOR_VERSION}AndroidGamepad
-
-load(qt_build_paths)
-CONFIG += java
-
-DESTDIR = $$MODULE_BASE_OUTDIR/jar
-
-API_VERSION = android-16
-
-JAVACLASSPATH += $$PWD/src
-
-JAVASOURCES += $$PWD/src/org/qtproject/qt/android/gamepad/QtGamepad.java
-
-# install
-target.path = $$[QT_INSTALL_PREFIX]/jar
-INSTALLS += target
-
-OTHER_FILES += $$JAVASOURCES
diff --git a/src/plugins/gamepads/android/jar/src/org/qtproject/qt/android/gamepad/QtGamepad.java b/src/plugins/gamepads/android/jar/src/org/qtproject/qt/android/gamepad/QtGamepad.java
deleted file mode 100644
index 94ffa9f..0000000
--- a/src/plugins/gamepads/android/jar/src/org/qtproject/qt/android/gamepad/QtGamepad.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 BogDan Vatra <bogdan@kde.org>
-** 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$
-**
-****************************************************************************/
-
-package org.qtproject.qt.android.gamepad;
-
-import android.app.Activity;
-import android.content.Context;
-import android.hardware.input.InputManager;
-import android.os.Handler;
-
-
-public class QtGamepad {
- private long m_nativePtr = 0;
- private InputManager m_manager;
- Activity m_activity;
- private InputManager.InputDeviceListener m_listener = new InputManager.InputDeviceListener() {
- @Override
- public void onInputDeviceAdded(int i) {
- synchronized (QtGamepad.this) {
- QtGamepad.onInputDeviceAdded(m_nativePtr, i);
- }
- }
-
- @Override
- public void onInputDeviceRemoved(int i) {
- synchronized (QtGamepad.this) {
- QtGamepad.onInputDeviceRemoved(m_nativePtr, i);
- }
- }
-
- @Override
- public void onInputDeviceChanged(int i) {
- synchronized (QtGamepad.this) {
- QtGamepad.onInputDeviceChanged(m_nativePtr, i);
- }
- }
- };
-
- QtGamepad(Activity activity)
- {
- m_manager = (InputManager) activity.getSystemService(Context.INPUT_SERVICE);
- m_activity = activity;
- }
-
- public void register(long nativePtr)
- {
- synchronized (this) {
- if (m_manager != null) {
- m_nativePtr = nativePtr;
- m_activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- m_manager.registerInputDeviceListener(m_listener, new Handler());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- });
- }
- }
- }
-
- public void unregister()
- {
- synchronized (this) {
- if (m_manager != null) {
- m_nativePtr = 0;
- m_manager.unregisterInputDeviceListener(m_listener);
- }
- }
- }
-
- private static native void onInputDeviceAdded(long nativePtr, int deviceId);
- private static native void onInputDeviceRemoved(long nativePtr, int deviceId);
- private static native void onInputDeviceChanged(long nativePtr, int deviceId);
-}
diff --git a/src/plugins/gamepads/android/src/main.cpp b/src/plugins/gamepads/android/src/main.cpp
deleted file mode 100644
index 97af630..0000000
--- a/src/plugins/gamepads/android/src/main.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** 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 "qandroidgamepadbackend_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidGamepadBackendPlugin : public QGamepadBackendPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "android.json")
-public:
- QGamepadBackend *create(const QString &key, const QStringList &paramList) override;
-};
-
-QGamepadBackend *QAndroidGamepadBackendPlugin::create(const QString &key, const QStringList &paramList) {
- Q_UNUSED(key);
- Q_UNUSED(paramList);
-
- return new QAndroidGamepadBackend();
-}
-
-QT_END_NAMESPACE
-
-#include "main.moc"
diff --git a/src/plugins/gamepads/android/src/qandroidgamepadbackend.cpp b/src/plugins/gamepads/android/src/qandroidgamepadbackend.cpp
deleted file mode 100644
index eae0e6a..0000000
--- a/src/plugins/gamepads/android/src/qandroidgamepadbackend.cpp
+++ /dev/null
@@ -1,635 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 BogDan Vatra <bogdan@kde.org>
-** 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 "qandroidgamepadbackend_p.h"
-
-#include <QtCore/QCoreApplication>
-#include <QtCore/QEvent>
-#include <QtCore/QPair>
-#include <QtCore/QThread>
-#include <QtCore/QList>
-
-#include <functional>
-#include <vector>
-
-#include <QVariant>
-#include <jni.h>
-#include <math.h>
-
-QT_BEGIN_NAMESPACE
-namespace {
- const QLatin1String AXES_KEY("axes");
- const QLatin1String BUTTONS_KEY("buttons");
-
- class FunctionEvent : public QEvent
- {
- typedef std::function<void()> Function;
- public:
- explicit FunctionEvent(const Function &func)
- : QEvent(QEvent::User)
- , m_function(func)
- {}
- inline void call() { m_function(); }
- static inline void runOnQtThread(QObject *receiver, const Function &func) {
- if (qApp->thread() == QThread::currentThread())
- func();
- else
- qApp->postEvent(receiver, new FunctionEvent(func));
- }
-
- private:
- Function m_function;
- };
-
- const char keyEventClass[] = "android/view/KeyEvent";
- inline int keyField(const char *field)
- {
- return QJNIObjectPrivate::getStaticField<jint>(keyEventClass, field);
- }
-
- const char motionEventClass[] = "android/view/MotionEvent";
- inline int motionField(const char *field)
- {
- return QJNIObjectPrivate::getStaticField<jint>(motionEventClass, field);
- }
-
- const char inputDeviceClass[] = "android/view/InputDevice";
- inline int inputDeviceField(const char *field)
- {
- return QJNIObjectPrivate::getStaticField<jint>(inputDeviceClass, field);
- }
-
-
- struct DefaultMapping : public QAndroidGamepadBackend::Mapping {
- DefaultMapping()
- {
- buttonsMap[keyField("KEYCODE_BUTTON_A")] = QGamepadManager::ButtonA;
- buttonsMap[keyField("KEYCODE_BUTTON_B")] = QGamepadManager::ButtonB;
- buttonsMap[keyField("KEYCODE_BUTTON_X")] = QGamepadManager::ButtonX;
- buttonsMap[keyField("KEYCODE_BUTTON_Y")] = QGamepadManager::ButtonY;
- buttonsMap[keyField("KEYCODE_BUTTON_L1")] = QGamepadManager::ButtonL1;
- buttonsMap[keyField("KEYCODE_BUTTON_R1")] = QGamepadManager::ButtonR1;
- buttonsMap[keyField("KEYCODE_BUTTON_L2")] = QGamepadManager::ButtonL2;
- buttonsMap[keyField("KEYCODE_BUTTON_R2")] = QGamepadManager::ButtonR2;
- buttonsMap[keyField("KEYCODE_BUTTON_SELECT")] = QGamepadManager::ButtonSelect;
- buttonsMap[keyField("KEYCODE_BUTTON_START")] = QGamepadManager::ButtonStart;
- buttonsMap[keyField("KEYCODE_BUTTON_THUMBL")] = QGamepadManager::ButtonL3;
- buttonsMap[keyField("KEYCODE_BUTTON_THUMBR")] = QGamepadManager::ButtonR3;
- buttonsMap[keyField("KEYCODE_DPAD_UP")] = QGamepadManager::ButtonUp;
- buttonsMap[keyField("KEYCODE_DPAD_DOWN")] = QGamepadManager::ButtonDown;
- buttonsMap[keyField("KEYCODE_DPAD_RIGHT")] = QGamepadManager::ButtonRight;
- buttonsMap[keyField("KEYCODE_DPAD_LEFT")] = QGamepadManager::ButtonLeft;
- buttonsMap[keyField("KEYCODE_DPAD_CENTER")] = QGamepadManager::ButtonCenter;
- buttonsMap[keyField("KEYCODE_BUTTON_MODE")] = QGamepadManager::ButtonGuide;
-
- if (QtAndroidPrivate::androidSdkVersion() >= 12) {
- axisMap[motionField("AXIS_X")].gamepadAxis = QGamepadManager::AxisLeftX;
- axisMap[motionField("AXIS_Y")].gamepadAxis = QGamepadManager::AxisLeftY;
- axisMap[motionField("AXIS_HAT_X")].gamepadAxis = QGamepadManager::AxisLeftX;
- axisMap[motionField("AXIS_HAT_Y")].gamepadAxis = QGamepadManager::AxisLeftY;
- axisMap[motionField("AXIS_Z")].gamepadAxis = QGamepadManager::AxisRightX;
- axisMap[motionField("AXIS_RZ")].gamepadAxis = QGamepadManager::AxisRightY;
- {
- auto &axis = axisMap[motionField("AXIS_LTRIGGER")];
- axis.gamepadAxis = QGamepadManager::AxisInvalid;
- axis.gamepadMinButton = QGamepadManager::ButtonL2;
- axis.gamepadMaxButton = QGamepadManager::ButtonL2;
- }
- {
- auto &axis = axisMap[motionField("AXIS_RTRIGGER")];
- axis.gamepadAxis = QGamepadManager::AxisInvalid;
- axis.gamepadMinButton = QGamepadManager::ButtonR2;
- axis.gamepadMaxButton = QGamepadManager::ButtonR2;
- }
-
- allAndroidAxes.push_back(motionField("AXIS_X"));
- allAndroidAxes.push_back(motionField("AXIS_Y"));
- allAndroidAxes.push_back(motionField("AXIS_Z"));
- allAndroidAxes.push_back(motionField("AXIS_RZ"));
- allAndroidAxes.push_back(motionField("AXIS_BRAKE"));
- allAndroidAxes.push_back(motionField("AXIS_GAS"));
-
- for (int i = 1; i < 16; ++i)
- allAndroidAxes.push_back(motionField(QByteArray("AXIS_GENERIC_").append(QByteArray::number(i)).constData()));
-
- allAndroidAxes.push_back(motionField("AXIS_HAT_X"));
- allAndroidAxes.push_back(motionField("AXIS_HAT_Y"));
- allAndroidAxes.push_back(motionField("AXIS_LTRIGGER"));
- allAndroidAxes.push_back(motionField("AXIS_RTRIGGER"));
- allAndroidAxes.push_back(motionField("AXIS_RUDDER"));
- allAndroidAxes.push_back(motionField("AXIS_THROTTLE"));
- allAndroidAxes.push_back(motionField("AXIS_WHEEL"));
- }
-
- if (QtAndroidPrivate::androidSdkVersion() >= 12) {
- acceptedSources.push_back(inputDeviceField("SOURCE_GAMEPAD"));
- acceptedSources.push_back(inputDeviceField("SOURCE_CLASS_JOYSTICK"));
- if (QtAndroidPrivate::androidSdkVersion() >= 21) {
- acceptedSources.push_back(inputDeviceField("SOURCE_HDMI"));
- }
- } else {
- acceptedSources.push_back(inputDeviceField("SOURCE_DPAD"));
- }
-
- ACTION_DOWN = keyField("ACTION_DOWN");
- ACTION_UP = keyField("ACTION_UP");
- ACTION_MULTIPLE = keyField("ACTION_MULTIPLE");
- FLAG_LONG_PRESS = keyField("FLAG_LONG_PRESS");
- }
- std::vector<int> acceptedSources;
- std::vector<int> allAndroidAxes;
- int ACTION_DOWN, ACTION_MULTIPLE, ACTION_UP, FLAG_LONG_PRESS;
- };
-
- void onInputDeviceAdded(JNIEnv *, jclass, jlong qtNativePtr, int deviceId)
- {
- if (!qtNativePtr)
- return;
- reinterpret_cast<QAndroidGamepadBackend*>(qtNativePtr)->addDevice(deviceId);
- }
- void onInputDeviceRemoved(JNIEnv *, jclass, jlong qtNativePtr, int deviceId)
- {
- if (!qtNativePtr)
- return;
- reinterpret_cast<QAndroidGamepadBackend*>(qtNativePtr)->removeDevice(deviceId);
- }
- void onInputDeviceChanged(JNIEnv *, jclass, jlong qtNativePtr, int deviceId)
- {
- if (!qtNativePtr)
- return;
- reinterpret_cast<QAndroidGamepadBackend*>(qtNativePtr)->updateDevice(deviceId);
- }
-
- static JNINativeMethod methods[] = {
- {"onInputDeviceAdded", "(JI)V", (void *)onInputDeviceAdded},
- {"onInputDeviceRemoved", "(JI)V", (void *)onInputDeviceRemoved},
- {"onInputDeviceChanged", "(JI)V", (void *)onInputDeviceChanged}
- };
-
- const char qtGamePadClassName[] = "org/qtproject/qt/android/gamepad/QtGamepad";
-
- inline void setAxisInfo(QJNIObjectPrivate &event, int axis, QAndroidGamepadBackend::Mapping::AndroidAxisInfo &info)
- {
- QJNIObjectPrivate device(event.callObjectMethod("getDevice", "()Landroid/view/InputDevice;"));
- if (device.isValid()) {
- const int source = event.callMethod<jint>("getSource", "()I");
- QJNIObjectPrivate motionRange = device.callObjectMethod("getMotionRange","(II)Landroid/view/InputDevice$MotionRange;", axis, source);
- if (motionRange.isValid()) {
- info.flatArea = motionRange.callMethod<jfloat>("getFlat", "()F");
- info.minValue = motionRange.callMethod<jfloat>("getMin", "()F");
- info.maxValue = motionRange.callMethod<jfloat>("getMax", "()F");
- info.fuzz = motionRange.callMethod<jfloat>("getFuzz", "()F");
- return;
- }
- }
- info.flatArea = 0;
- }
-
-} // namespace
-
-Q_GLOBAL_STATIC(DefaultMapping, g_defaultMapping)
-
-void QAndroidGamepadBackend::Mapping::AndroidAxisInfo::restoreSavedData(const QVariantMap &value)
-{
- gamepadAxis = QGamepadManager::GamepadAxis(value[QLatin1String("axis")].toInt());
- gamepadMinButton = QGamepadManager::GamepadButton(value[QLatin1String("minButton")].toInt());
- gamepadMaxButton = QGamepadManager::GamepadButton(value[QLatin1String("maxButton")].toInt());
-}
-
-QVariantMap QAndroidGamepadBackend::Mapping::AndroidAxisInfo::dataToSave() const
-{
- QVariantMap data;
- data[QLatin1String("axis")] = gamepadAxis;
- data[QLatin1String("minButton")] = gamepadMinButton;
- data[QLatin1String("maxButton")] = gamepadMaxButton;
- return data;
-}
-
-QAndroidGamepadBackend::QAndroidGamepadBackend(QObject *parent)
- : QGamepadBackend(parent)
-{
- QtAndroidPrivate::registerGenericMotionEventListener(this);
- QtAndroidPrivate::registerKeyEventListener(this);
-}
-
-QAndroidGamepadBackend::~QAndroidGamepadBackend()
-{
- QtAndroidPrivate::unregisterGenericMotionEventListener(this);
- QtAndroidPrivate::unregisterKeyEventListener(this);
-}
-
-void QAndroidGamepadBackend::addDevice(int deviceId)
-{
- if (deviceId == -1)
- return;
-
- QMutexLocker lock(&m_mutex);
- QJNIObjectPrivate inputDevice = QJNIObjectPrivate::callStaticObjectMethod(inputDeviceClass, "getDevice", "(I)Landroid/view/InputDevice;", deviceId);
- int sources = inputDevice.callMethod<jint>("getSources", "()I");
- bool acceptable = false;
- for (int source : g_defaultMapping()->acceptedSources) {
- if ( (source & sources) == source) {
- acceptable = true;
- break;
- }
- }
-
- if (acceptable) {
- m_devices.insert(deviceId, *g_defaultMapping());
- int productId = qHash(inputDevice.callObjectMethod("getDescriptor", "()Ljava/lang/String;").toString());
- m_devices[deviceId].productId = productId;
- if (productId) {
- QVariant settings = readSettings(productId);
- if (!settings.isNull()) {
- auto &deviceInfo = m_devices[deviceId];
- deviceInfo.needsConfigure = false;
-
- QVariantMap data = settings.toMap()[AXES_KEY].toMap();
- for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
- deviceInfo.axisMap[it.key().toInt()].restoreSavedData(it.value().toMap());
-
- data = settings.toMap()[BUTTONS_KEY].toMap();
- for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
- deviceInfo.buttonsMap[it.key().toInt()] = QGamepadManager::GamepadButton(it.value().toInt());
- }
- }
- FunctionEvent::runOnQtThread(this, [this, deviceId]{
- emit gamepadAdded(deviceId);
- });
- }
-}
-
-void QAndroidGamepadBackend::updateDevice(int deviceId)
-{
- Q_UNUSED(deviceId);
- //QMutexLocker lock(&m_mutex);
-}
-
-void QAndroidGamepadBackend::removeDevice(int deviceId)
-{
- QMutexLocker lock(&m_mutex);
- if (m_devices.remove(deviceId)) {
- FunctionEvent::runOnQtThread(this, [this, deviceId]{
- emit gamepadRemoved(deviceId);
- });
- }
-}
-
-bool QAndroidGamepadBackend::event(QEvent *ev)
-{
- if (ev->type() == QEvent::User) {
- static_cast<FunctionEvent*>(ev)->call();
- return true;
- }
- return QGamepadBackend::event(ev);
-}
-
-bool QAndroidGamepadBackend::isConfigurationNeeded(int deviceId)
-{
- QMutexLocker lock(&m_mutex);
- auto it = m_devices.find(deviceId);
- if (it == m_devices.end())
- return false;
- return it->needsConfigure;
-}
-
-bool QAndroidGamepadBackend::configureButton(int deviceId, QGamepadManager::GamepadButton button)
-{
- QMutexLocker lock(&m_mutex);
- auto it = m_devices.find(deviceId);
- if (it == m_devices.end())
- return false;
-
- it.value().calibrateButton = button;
- return true;
-}
-
-bool QAndroidGamepadBackend::configureAxis(int deviceId, QGamepadManager::GamepadAxis axis)
-{
- QMutexLocker lock(&m_mutex);
- auto it = m_devices.find(deviceId);
- if (it == m_devices.end())
- return false;
-
- it.value().calibrateAxis = axis;
- return true;
-}
-
-bool QAndroidGamepadBackend::setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button)
-{
- QMutexLocker lock(&m_mutex);
- auto it = m_devices.find(deviceId);
- if (it == m_devices.end())
- return false;
-
- it.value().cancelConfigurationButton = button;
- return true;
-}
-
-void QAndroidGamepadBackend::resetConfiguration(int deviceId)
-{
- QMutexLocker lock(&m_mutex);
- auto it = m_devices.find(deviceId);
- if (it == m_devices.end())
- return;
-
- it.value().axisMap.clear();
- it.value().buttonsMap.clear();
- it.value().calibrateButton = QGamepadManager::ButtonInvalid;
- it.value().calibrateAxis = QGamepadManager::AxisInvalid;
- it.value().cancelConfigurationButton = QGamepadManager::ButtonInvalid;
- it.value().needsConfigure = false;
-}
-
-bool QAndroidGamepadBackend::handleKeyEvent(jobject event)
-{
- QJNIObjectPrivate ev(event);
- QMutexLocker lock(&m_mutex);
- const int deviceId = ev.callMethod<jint>("getDeviceId", "()I");
- const auto deviceIt = m_devices.find(deviceId);
- if (deviceIt == m_devices.end())
- return false;
-
- const int action = ev.callMethod<jint>("getAction", "()I");
- if (action != g_defaultMapping()->ACTION_DOWN &&
- action != g_defaultMapping()->ACTION_UP) {
- return false;
- }
- const int flags = ev.callMethod<jint>("getFlags", "()I");
- if ((flags & g_defaultMapping()->FLAG_LONG_PRESS) == g_defaultMapping()->FLAG_LONG_PRESS)
- return false;
-
- const int keyCode = ev.callMethod<jint>("getKeyCode", "()I");
- auto &deviceMap = deviceIt.value();
-
- if (deviceMap.cancelConfigurationButton != QGamepadManager::ButtonInvalid &&
- (deviceMap.calibrateButton != QGamepadManager::ButtonInvalid ||
- deviceMap.calibrateAxis != QGamepadManager::AxisInvalid)) {
-
- const auto buttonsMapIt = deviceMap.buttonsMap.find(keyCode);
- if (buttonsMapIt != deviceMap.buttonsMap.end() &&
- deviceMap.cancelConfigurationButton == buttonsMapIt.value()) {
- deviceMap.calibrateButton = QGamepadManager::ButtonInvalid;
- deviceMap.calibrateAxis = QGamepadManager::AxisInvalid;
- FunctionEvent::runOnQtThread(this, [this, deviceId]{
- emit configurationCanceled(deviceId);
- });
- return true;
- }
- }
-
- if (deviceMap.calibrateButton != QGamepadManager::ButtonInvalid) {
- deviceMap.buttonsMap[keyCode] = deviceMap.calibrateButton;
- auto but = deviceMap.calibrateButton;
- deviceMap.calibrateButton = QGamepadManager::ButtonInvalid;
- saveData(deviceMap);
- FunctionEvent::runOnQtThread(this, [this, deviceId, but]{
- emit buttonConfigured(deviceId, but);
- });
- }
-
- const auto buttonsMapIt = deviceMap.buttonsMap.find(keyCode);
- if (buttonsMapIt == deviceMap.buttonsMap.end())
- return false;
-
- const auto button = buttonsMapIt.value();
- if (action == g_defaultMapping()->ACTION_DOWN) {
- FunctionEvent::runOnQtThread(this, [this, deviceId, button]{
- emit gamepadButtonPressed(deviceId, button, 1.0);
- });
- } else {
- FunctionEvent::runOnQtThread(this, [this, deviceId, button]{
- emit gamepadButtonReleased(deviceId, button);
- });
- }
- return true;
-}
-
-bool QAndroidGamepadBackend::handleGenericMotionEvent(jobject event)
-{
- // GenericMotionEvent was introduced in API-12
- Q_ASSERT(QtAndroidPrivate::androidSdkVersion() >= 12);
-
- QJNIObjectPrivate ev(event);
- QMutexLocker lock(&m_mutex);
- const int deviceId = ev.callMethod<jint>("getDeviceId", "()I");
- const auto deviceIt = m_devices.find(deviceId);
- if (deviceIt == m_devices.end())
- return false;
-
- auto &deviceMap = deviceIt.value();
- if (deviceMap.calibrateAxis != QGamepadManager::AxisInvalid ||
- deviceMap.calibrateButton != QGamepadManager::ButtonInvalid) {
- double lastValue = 0;
- int lastAxis = -1;
- for (int axis : g_defaultMapping()->allAndroidAxes) {
- double value = ev.callMethod<jfloat>("getAxisValue", "(I)F", axis);
- if (fabs(value) > fabs(lastValue)) {
- lastValue = value;
- lastAxis = axis;
- }
- }
-
- if (!lastValue || lastAxis == -1)
- return false;
-
- if (deviceMap.calibrateAxis != QGamepadManager::AxisInvalid) {
- deviceMap.axisMap[lastAxis].gamepadAxis = deviceMap.calibrateAxis;
- auto axis = deviceMap.calibrateAxis;
- deviceMap.calibrateAxis = QGamepadManager::AxisInvalid;
- saveData(deviceMap);
- FunctionEvent::runOnQtThread(this, [this, deviceId, axis]{
- emit axisConfigured(deviceId, axis);
- });
- } else if (deviceMap.calibrateButton != QGamepadManager::ButtonInvalid &&
- deviceMap.calibrateButton != QGamepadManager::ButtonUp &&
- deviceMap.calibrateButton != QGamepadManager::ButtonDown &&
- deviceMap.calibrateButton != QGamepadManager::ButtonLeft &&
- deviceMap.calibrateButton != QGamepadManager::ButtonRight) {
- auto &axis = deviceMap.axisMap[lastAxis];
- axis.gamepadAxis = QGamepadManager::AxisInvalid;
- setAxisInfo(ev, lastAxis, axis);
- bool save = false;
- if (lastValue == axis.minValue) {
- axis.gamepadMinButton = deviceMap.calibrateButton;
- if (axis.gamepadMaxButton == QGamepadManager::ButtonInvalid)
- axis.gamepadMaxButton = deviceMap.calibrateButton;
- save = true;
- } else if (lastValue == axis.maxValue) {
- axis.gamepadMaxButton = deviceMap.calibrateButton;
- if (axis.gamepadMinButton == QGamepadManager::ButtonInvalid)
- axis.gamepadMinButton = deviceMap.calibrateButton;
- save = true;
- }
-
- if (save) {
- auto but = deviceMap.calibrateButton;
- deviceMap.calibrateButton = QGamepadManager::ButtonInvalid;
- saveData(deviceMap);
- FunctionEvent::runOnQtThread(this, [this, deviceId, but]{
- emit buttonConfigured(deviceId, but);
- });
- }
- }
- }
-
- typedef QPair<QGamepadManager::GamepadAxis, double> GamepadAxisValue;
- QList<GamepadAxisValue> axisValues;
- typedef QPair<QGamepadManager::GamepadButton, double> GamepadButtonValue;
- QList<GamepadButtonValue> buttonValues;
- auto setValue = [&axisValues, &buttonValues](Mapping::AndroidAxisInfo &axisInfo, double value) {
- if (axisInfo.setValue(value)) {
- if (axisInfo.gamepadAxis != QGamepadManager::AxisInvalid) {
- axisValues.push_back(GamepadAxisValue(axisInfo.gamepadAxis, axisInfo.lastValue));
- } else {
- if (axisInfo.lastValue < 0) {
- buttonValues.push_back(GamepadButtonValue(axisInfo.gamepadMinButton, axisInfo.lastValue));
- axisInfo.gamepadLastButton = axisInfo.gamepadMinButton;
- } else if (axisInfo.lastValue > 0) {
- buttonValues.push_back(GamepadButtonValue(axisInfo.gamepadMaxButton, axisInfo.lastValue));
- axisInfo.gamepadLastButton = axisInfo.gamepadMaxButton;
- } else {
- buttonValues.push_back(GamepadButtonValue(axisInfo.gamepadLastButton, 0.0));
- axisInfo.gamepadLastButton = QGamepadManager::ButtonInvalid;
- }
- }
- }
- };
- for (auto it = deviceMap.axisMap.begin(); it != deviceMap.axisMap.end(); ++it) {
- auto &axisInfo = it.value();
- if (axisInfo.flatArea == -1)
- setAxisInfo(ev, it.key(), axisInfo);
- const int historicalValues = ev.callMethod<jint>("getHistorySize", "()I");
- for (int i = 0; i < historicalValues; ++i) {
- double value = ev.callMethod<jfloat>("getHistoricalAxisValue", "(II)F", it.key(), i);
- setValue(axisInfo, value);
- }
- double value = ev.callMethod<jfloat>("getAxisValue", "(I)F", it.key());
- setValue(axisInfo, value);
- }
-
- if (!axisValues.isEmpty()) {
- FunctionEvent::runOnQtThread(this, [this, deviceId, axisValues]{
- for (const auto &axisValue : axisValues)
- emit gamepadAxisMoved(deviceId, axisValue.first, axisValue.second);
- });
- }
-
- if (!buttonValues.isEmpty()) {
- FunctionEvent::runOnQtThread(this, [this, deviceId, buttonValues]{
- for (const auto &buttonValue : buttonValues)
- if (buttonValue.second)
- emit gamepadButtonPressed(deviceId, buttonValue.first, fabs(buttonValue.second));
- else
- emit gamepadButtonReleased(deviceId, buttonValue.first);
- });
- }
-
- return false;
-}
-
-bool QAndroidGamepadBackend::start()
-{
- {
- QMutexLocker lock(&m_mutex);
- if (QtAndroidPrivate::androidSdkVersion() >= 16) {
- if (!m_qtGamepad.isValid())
- m_qtGamepad = QJNIObjectPrivate(qtGamePadClassName, "(Landroid/app/Activity;)V", QtAndroidPrivate::activity());
- m_qtGamepad.callMethod<void>("register", "(J)V", jlong(this));
- }
- }
-
- QJNIObjectPrivate ids = QJNIObjectPrivate::callStaticObjectMethod(inputDeviceClass, "getDeviceIds", "()[I");
- jintArray jarr = jintArray(ids.object());
- QJNIEnvironmentPrivate env;
- size_t sz = env->GetArrayLength(jarr);
- jint *buff = env->GetIntArrayElements(jarr, nullptr);
- for (size_t i = 0; i < sz; ++i)
- addDevice(buff[i]);
- env->ReleaseIntArrayElements(jarr, buff, 0);
- return true;
-}
-
-void QAndroidGamepadBackend::stop()
-{
- QMutexLocker lock(&m_mutex);
- if (QtAndroidPrivate::androidSdkVersion() >= 16 && m_qtGamepad.isValid())
- m_qtGamepad.callMethod<void>("unregister", "()V");
-}
-
-void QAndroidGamepadBackend::saveData(const QAndroidGamepadBackend::Mapping &deviceInfo)
-{
- if (!deviceInfo.productId)
- return ;
-
- QVariantMap settings, data;
- for (auto it = deviceInfo.axisMap.begin(); it != deviceInfo.axisMap.end(); ++it)
- data[QString::number(it.key())] = it.value().dataToSave();
- settings[AXES_KEY] = data;
-
- data.clear();
- for (auto it = deviceInfo.buttonsMap.begin(); it != deviceInfo.buttonsMap.end(); ++it)
- data[QString::number(it.key())] = it.value();
- settings[BUTTONS_KEY] = data;
-
- saveSettings(deviceInfo.productId, settings);
-}
-
-JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
-{
- static bool initialized = false;
- if (initialized)
- return JNI_VERSION_1_6;
- initialized = true;
-
- JNIEnv* env;
- // get the JNIEnv pointer.
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
- return JNI_ERR;
-
- // search for Java class which declares the native methods
- jclass javaClass = env->FindClass("org/qtproject/qt/android/gamepad/QtGamepad");
- if (!javaClass)
- return JNI_ERR;
-
- // register our native methods
- if (env->RegisterNatives(javaClass, methods,
- sizeof(methods) / sizeof(methods[0])) < 0) {
- return JNI_ERR;
- }
- return JNI_VERSION_1_6;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gamepads/android/src/qandroidgamepadbackend_p.h b/src/plugins/gamepads/android/src/qandroidgamepadbackend_p.h
deleted file mode 100644
index b54f569..0000000
--- a/src/plugins/gamepads/android/src/qandroidgamepadbackend_p.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 BogDan Vatra <bogdan@kde.org>
-** 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 QANDROIDGAMEPADBACKEND_P_H
-#define QANDROIDGAMEPADBACKEND_P_H
-
-#include <QtCore/QHash>
-#include <QtCore/QMutex>
-#include <QtCore/QSet>
-
-#include <QtCore/private/qjni_p.h>
-#include <QtCore/private/qjnihelpers_p.h>
-
-#include <QtGamepad/QGamepadManager>
-#include <QtGamepad/private/qgamepadbackend_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidGamepadBackend : public QGamepadBackend, public QtAndroidPrivate::GenericMotionEventListener, public QtAndroidPrivate::KeyEventListener
-{
- Q_OBJECT
-public:
- explicit QAndroidGamepadBackend(QObject *parent = nullptr);
- ~QAndroidGamepadBackend();
-
- void addDevice(int deviceId);
- void updateDevice(int deviceId);
- void removeDevice(int deviceId);
-
- // QObject interface
- bool event(QEvent *) override;
-
- // QGamepadBackend interface
- bool isConfigurationNeeded(int deviceId) override;
- bool configureButton(int deviceId, QGamepadManager::GamepadButton button) override;
- bool configureAxis(int deviceId, QGamepadManager::GamepadAxis axis) override;
- bool setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button) override;
- void resetConfiguration(int deviceId) override;
-
-
- // KeyEventListener interface
- bool handleKeyEvent(jobject event) override;
-
- // GenericMotionEventListener interface
- bool handleGenericMotionEvent(jobject event) override;
-
-protected:
- bool start() override;
- void stop() override;
-
-public:
- struct Mapping {
- struct AndroidAxisInfo : public AxisInfo<double> {
- AndroidAxisInfo() : AxisInfo(-1.0, 1.0) { }
- AndroidAxisInfo(double minValue, double maxValue) : AxisInfo(minValue, maxValue) { }
-
- inline bool setValue(double value)
- {
- if (minValue != -1.0 && maxValue != 1.0)
- value = AxisInfo::normalized(value);
-
- if (qAbs(value) <= flatArea)
- value = 0;
-
- if (qAbs(qAbs(value) - qAbs(lastValue)) <= fuzz)
- return false;
-
- lastValue = value;
- return true;
- }
- void restoreSavedData(const QVariantMap &value);
- QVariantMap dataToSave() const;
-
- double flatArea = -1;
- double fuzz = 0;
- double lastValue = 0;
- QGamepadManager::GamepadButton gamepadMinButton = QGamepadManager::ButtonInvalid;
- QGamepadManager::GamepadButton gamepadMaxButton = QGamepadManager::ButtonInvalid;
- QGamepadManager::GamepadButton gamepadLastButton = QGamepadManager::ButtonInvalid;
- };
- QHash<int, AndroidAxisInfo> axisMap;
- QHash<int, QGamepadManager::GamepadButton> buttonsMap;
-
- QGamepadManager::GamepadButton calibrateButton = QGamepadManager::ButtonInvalid;
- QGamepadManager::GamepadAxis calibrateAxis = QGamepadManager::AxisInvalid;
- QGamepadManager::GamepadButton cancelConfigurationButton = QGamepadManager::ButtonInvalid;
- int productId = 0;
- bool needsConfigure = false;
- };
-
-private:
- void saveData(const Mapping &deviceInfo);
-
-private:
- QMutex m_mutex;
- QJNIObjectPrivate m_qtGamepad;
- QHash<int, Mapping> m_devices;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDGAMEPADBACKEND_P_H
diff --git a/src/plugins/gamepads/android/src/src.pro b/src/plugins/gamepads/android/src/src.pro
deleted file mode 100644
index 31f8a28..0000000
--- a/src/plugins/gamepads/android/src/src.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-TARGET = androidgamepad
-QT += core-private gamepad gamepad-private
-
-PLUGIN_TYPE = gamepads
-PLUGIN_CLASS_NAME = QAndroidGamepadBackendPlugin
-
-load(qt_plugin)
-
-HEADERS += \
- qandroidgamepadbackend_p.h
-
-SOURCES += \
- main.cpp \
- qandroidgamepadbackend.cpp
-
-DISTFILES += \
- android.json
diff --git a/src/plugins/gamepads/darwin/darwin.json b/src/plugins/gamepads/darwin/darwin.json
deleted file mode 100644
index f72350b..0000000
--- a/src/plugins/gamepads/darwin/darwin.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": [ "darwin" ]
-}
diff --git a/src/plugins/gamepads/darwin/darwin.pro b/src/plugins/gamepads/darwin/darwin.pro
deleted file mode 100644
index 9a35c75..0000000
--- a/src/plugins/gamepads/darwin/darwin.pro
+++ /dev/null
@@ -1,19 +0,0 @@
-TARGET = darwingamepad
-QT += gamepad gamepad-private
-
-PLUGIN_TYPE = gamepads
-PLUGIN_EXTENDS = gamepad
-PLUGIN_CLASS_NAME = QDarwinGamepadBackendPlugin
-load(qt_plugin)
-
-LIBS += -framework GameController -framework Foundation
-
-HEADERS += qdarwingamepadbackend_p.h
-OBJECTIVE_SOURCES += \
- qdarwingamepadbackend.mm
-
-SOURCES += \
- main.cpp
-
-OTHER_FILES += \
- darwin.json
diff --git a/src/plugins/gamepads/darwin/main.cpp b/src/plugins/gamepads/darwin/main.cpp
deleted file mode 100644
index 87afec1..0000000
--- a/src/plugins/gamepads/darwin/main.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** 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 "qdarwingamepadbackend_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QDarwinGamepadBackendPlugin : public QGamepadBackendPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "darwin.json")
-public:
- QGamepadBackend *create(const QString &key, const QStringList &paramList) override;
-};
-
-QGamepadBackend *QDarwinGamepadBackendPlugin::create(const QString &key, const QStringList &paramList) {
- Q_UNUSED(key);
- Q_UNUSED(paramList);
-
- return new QDarwinGamepadBackend();
-}
-
-QT_END_NAMESPACE
-
-#include "main.moc"
diff --git a/src/plugins/gamepads/darwin/qdarwingamepadbackend.mm b/src/plugins/gamepads/darwin/qdarwingamepadbackend.mm
deleted file mode 100644
index 6094a99..0000000
--- a/src/plugins/gamepads/darwin/qdarwingamepadbackend.mm
+++ /dev/null
@@ -1,570 +0,0 @@
-/****************************************************************************
-**
-** 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 "qdarwingamepadbackend_p.h"
-
-#include <QtCore/QDebug>
-
-#import <GameController/GameController.h>
-
-@interface QT_MANGLE_NAMESPACE(DarwinGamepadManager) : NSObject
-
-@property (nonatomic, strong) id connectObserver;
-@property (nonatomic, strong) id disconnectObserver;
-
-@end
-
-@implementation QT_MANGLE_NAMESPACE(DarwinGamepadManager)
-{
- QDarwinGamepadBackend *backend;
- NSMutableArray *connectedControllers;
-}
-
--(instancetype)initWithBackend:(QDarwinGamepadBackend *)gamepadBackend
-{
- if ((self = [self init])) {
- backend = gamepadBackend;
- connectedControllers = [[NSMutableArray alloc] init];
- //Setup observers for monitoring controller connections/disconnections
- self.connectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification
- object:nil
- queue:[NSOperationQueue mainQueue]
- usingBlock:^(NSNotification *note) {
- GCController *controller = (GCController*)note.object;
- [self addMonitoredController:controller];
-
- }];
- self.disconnectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidDisconnectNotification
- object:nil
- queue:[NSOperationQueue mainQueue]
- usingBlock:^(NSNotification *note) {
- GCController *controller = (GCController*)note.object;
- [self removeMonitoredController:controller];
- }];
- //Set initial controller values
- for (int i = 0; i < 4; ++i)
- [connectedControllers addObject:[NSNull null]];
-
- //Add monitoring for any alrready connected controllers
- for (NSUInteger i = 0; i < [[GCController controllers] count]; ++i) {
- [self addMonitoredController:[GCController controllers][i]];
- }
- }
- return self;
-}
-
--(void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self.connectObserver];
- [[NSNotificationCenter defaultCenter] removeObserver:self.disconnectObserver];
- [connectedControllers release];
- [super dealloc];
-}
-
--(void)addMonitoredController:(GCController *)controller
-{
- int index = -1;
- for (int i = 0; i < 4; ++i) {
- if (connectedControllers[i] == [NSNull null]) {
- [connectedControllers replaceObjectAtIndex: i withObject: controller];
- index = i;
- break;
- }
- }
-
- controller.playerIndex = GCControllerPlayerIndex(index);
-
- QMetaObject::invokeMethod(backend, "darwinGamepadAdded", Qt::AutoConnection, Q_ARG(int, index));
-
- //Pause button handler
- [controller setControllerPausedHandler:^(GCController *controller) {
- Q_UNUSED(controller);
- QMetaObject::invokeMethod(backend, "handlePauseButton", Qt::AutoConnection, Q_ARG(int, index));
- }];
-
- if (controller.extendedGamepad) {
- //leftShoulder
- [controller.extendedGamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonL1),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonL1));
- }
- }];
- //rightShoulder
- [controller.extendedGamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonR1),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonR1));
- }
- }];
- //dpad
- [controller.extendedGamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
- Q_UNUSED(dpad);
- if (xValue > 0) {
- //right
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonRight),
- Q_ARG(double, 1));
- } else if (xValue < 0) {
- //left
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonLeft),
- Q_ARG(double, 1));
- } else {
- //released
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonRight));
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonLeft));
- }
- if (yValue > 0) {
- //up
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonUp),
- Q_ARG(double, 1));
- } else if (yValue < 0) {
- //down
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonDown),
- Q_ARG(double, 1));
- } else {
- //released
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonUp));
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonDown));
- }
- }];
- //buttonA
- [controller.extendedGamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonA),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonA));
- }
- }];
- //buttonB
- [controller.extendedGamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonB),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonB));
- }
- }];
- //buttonX
- [controller.extendedGamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonX),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonX));
- }
- }];
- //buttonY
- [controller.extendedGamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- //Invoke slot
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonY),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonY));
- }
- }];
-
- //leftThumbstick
- [controller.extendedGamepad.leftThumbstick setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
- Q_UNUSED(dpad);
- QMetaObject::invokeMethod(backend, "darwinGamepadAxisMoved", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadAxis, QGamepadManager::AxisLeftX),
- Q_ARG(double, xValue));
- QMetaObject::invokeMethod(backend, "darwinGamepadAxisMoved", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadAxis, QGamepadManager::AxisLeftY),
- Q_ARG(double, -yValue));
- }];
- //rightTumbstick
- [controller.extendedGamepad.rightThumbstick setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
- Q_UNUSED(dpad);
- QMetaObject::invokeMethod(backend, "darwinGamepadAxisMoved", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadAxis, QGamepadManager::AxisRightX),
- Q_ARG(double, xValue));
- QMetaObject::invokeMethod(backend, "darwinGamepadAxisMoved", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadAxis, QGamepadManager::AxisRightY),
- Q_ARG(double, -yValue));
- }];
- //leftTrigger
- [controller.extendedGamepad.leftTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonL2),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonL2));
- }
- }];
- //rightTrigger
- [controller.extendedGamepad.rightTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonR2),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonR2));
- }
-
- }];
- } else if (controller.gamepad) {
- //leftShoulder
- [controller.gamepad.leftShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonL1),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonL1));
- }
- }];
- //rightShoulder
- [controller.gamepad.rightShoulder setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonR1),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonR1));
- }
- }];
- //dpad
- [controller.gamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
- Q_UNUSED(dpad);
- if (xValue > 0) {
- //right
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonRight),
- Q_ARG(double, 1));
- } else if (xValue < 0) {
- //left
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonLeft),
- Q_ARG(double, 1));
- } else {
- //released
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonRight));
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonLeft));
- }
- if (yValue > 0) {
- //up
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonUp),
- Q_ARG(double, 1));
- } else if (yValue < 0) {
- //down
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonDown),
- Q_ARG(double, 1));
- } else {
- //released
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonUp));
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonDown));
- }
- }];
- //buttonA
- [controller.gamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonA),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonA));
- }
- }];
- //buttonB
- [controller.gamepad.buttonB setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonB),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonB));
- }
- }];
- //buttonX
- [controller.gamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonX),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonX));
- }
- }];
- //buttonY
- [controller.gamepad.buttonY setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonY),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonY));
- }
- }];
- }
-#ifdef Q_OS_TVOS
- else if (controller.microGamepad) {
- //leftThumbstick
- [controller.microGamepad.dpad setValueChangedHandler:^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
- Q_UNUSED(dpad);
- QMetaObject::invokeMethod(backend, "darwinGamepadAxisMoved", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadAxis, QGamepadManager::AxisLeftX),
- Q_ARG(double, xValue));
- QMetaObject::invokeMethod(backend, "darwinGamepadAxisMoved", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadAxis, QGamepadManager::AxisLeftY),
- Q_ARG(double, -yValue));
- }];
- //buttonA
- [controller.microGamepad.buttonA setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonA),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonA));
- }
- }];
- //buttonX
- [controller.microGamepad.buttonX setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
- Q_UNUSED(button);
- if (pressed) {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonPressed", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonX),
- Q_ARG(double, value));
- } else {
- QMetaObject::invokeMethod(backend, "darwinGamepadButtonReleased", Qt::AutoConnection,
- Q_ARG(int, index),
- Q_ARG(QGamepadManager::GamepadButton, QGamepadManager::ButtonX));
- }
- }];
- }
-#endif
-}
-
--(void)removeMonitoredController:(GCController *)controller
-{
- int index = -1;
- for (int i = 0; i < 4; ++i) {
- if (connectedControllers[i] == controller) {
- [connectedControllers replaceObjectAtIndex: i withObject: [NSNull null]];
- index = i;
- break;
- }
- }
-
- QMetaObject::invokeMethod(backend, "darwinGamepadRemoved", Qt::AutoConnection, Q_ARG(int, index));
-}
-
-@end
-
-QT_BEGIN_NAMESPACE
-
-QDarwinGamepadBackend::QDarwinGamepadBackend(QObject *parent)
- : QGamepadBackend(parent)
- , m_darwinGamepadManager(nullptr)
- , m_isMonitoringActive(false)
-{
- m_darwinGamepadManager = [[QT_MANGLE_NAMESPACE(DarwinGamepadManager) alloc] initWithBackend:this];
-}
-
-QDarwinGamepadBackend::~QDarwinGamepadBackend()
-{
- [m_darwinGamepadManager release];
-}
-
-bool QDarwinGamepadBackend::start()
-{
- m_isMonitoringActive = true;
- return true;
-}
-
-void QDarwinGamepadBackend::stop()
-{
- m_isMonitoringActive = false;
-}
-
-void QDarwinGamepadBackend::darwinGamepadAdded(int index)
-{
- if (m_isMonitoringActive) {
- emit gamepadAdded(index);
- m_pauseButtonMap.insert(index, false);
- }
-}
-
-void QDarwinGamepadBackend::darwinGamepadRemoved(int index)
-{
- if (m_isMonitoringActive) {
- emit gamepadRemoved(index);
- m_pauseButtonMap.remove(index);
- }
-}
-
-void QDarwinGamepadBackend::darwinGamepadAxisMoved(int index, QGamepadManager::GamepadAxis axis, double value)
-{
- if (m_isMonitoringActive)
- emit gamepadAxisMoved(index, axis, value);
-}
-
-void QDarwinGamepadBackend::darwinGamepadButtonPressed(int index, QGamepadManager::GamepadButton button, double value)
-{
- if (m_isMonitoringActive)
- emit gamepadButtonPressed(index, button, value);
-}
-
-void QDarwinGamepadBackend::darwinGamepadButtonReleased(int index, QGamepadManager::GamepadButton button)
-{
- if (m_isMonitoringActive)
- emit gamepadButtonReleased(index, button);
-}
-
-void QDarwinGamepadBackend::handlePauseButton(int index)
-{
- //If already pressed
- if (m_pauseButtonMap.value(index)) {
- emit gamepadButtonReleased(index, QGamepadManager::ButtonStart);
- m_pauseButtonMap[index] = false;
- } else {
- //If not currently pressed
- emit gamepadButtonPressed(index, QGamepadManager::ButtonStart, 1.0);
- m_pauseButtonMap[index] = true;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gamepads/darwin/qdarwingamepadbackend_p.h b/src/plugins/gamepads/darwin/qdarwingamepadbackend_p.h
deleted file mode 100644
index 326ccaf..0000000
--- a/src/plugins/gamepads/darwin/qdarwingamepadbackend_p.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** 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 QDARWINGAMEPADBACKEND_P_H
-#define QDARWINGAMEPADBACKEND_P_H
-
-#include <QtCore/QTimer>
-#include <QtCore/QMap>
-
-#include <QtGamepad/QGamepadManager>
-#include <QtGamepad/private/qgamepadbackend_p.h>
-
-Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(DarwinGamepadManager));
-
-QT_BEGIN_NAMESPACE
-
-class QDarwinGamepadBackend : public QGamepadBackend
-{
- Q_OBJECT
-public:
- explicit QDarwinGamepadBackend(QObject *parent = nullptr);
- ~QDarwinGamepadBackend();
-
-protected:
- bool start() override;
- void stop() override;
-
-public Q_SLOTS:
- void darwinGamepadAdded(int index);
- void darwinGamepadRemoved(int index);
- void darwinGamepadAxisMoved(int index, QGamepadManager::GamepadAxis axis, double value);
- void darwinGamepadButtonPressed(int index, QGamepadManager::GamepadButton button, double value);
- void darwinGamepadButtonReleased(int index, QGamepadManager::GamepadButton button);
- void handlePauseButton(int index);
-
-private:
- QT_MANGLE_NAMESPACE(DarwinGamepadManager) *m_darwinGamepadManager;
- bool m_isMonitoringActive;
- QMap<int, bool> m_pauseButtonMap;
-};
-
-QT_END_NAMESPACE
-
-#endif // QDARWINGAMEPADBACKEND_P_H
diff --git a/src/plugins/gamepads/evdev/evdev.json b/src/plugins/gamepads/evdev/evdev.json
deleted file mode 100644
index 90c6d7d..0000000
--- a/src/plugins/gamepads/evdev/evdev.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": [ "evdev" ]
-}
diff --git a/src/plugins/gamepads/evdev/evdev.pro b/src/plugins/gamepads/evdev/evdev.pro
deleted file mode 100644
index d819de4..0000000
--- a/src/plugins/gamepads/evdev/evdev.pro
+++ /dev/null
@@ -1,14 +0,0 @@
-TARGET = evdevgamepad
-QT += core-private devicediscovery_support-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
deleted file mode 100644
index 1832ea5..0000000
--- a/src/plugins/gamepads/evdev/main.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Gamepad module
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtGamepad/private/qgamepadbackendfactory_p.h>
-#include <QtGamepad/private/qgamepadbackendplugin_p.h>
-
-#include "qevdevgamepadbackend_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QEvdevGamepadBackendPlugin : public QGamepadBackendPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "evdev.json")
-public:
- QGamepadBackend *create(const QString &key, const QStringList &paramList) override;
-};
-
-QGamepadBackend *QEvdevGamepadBackendPlugin::create(const QString &key, const QStringList &paramList)
-{
- Q_UNUSED(key);
- Q_UNUSED(paramList);
-
- return new QEvdevGamepadBackend;
-}
-
-QT_END_NAMESPACE
-
-#include "main.moc"
diff --git a/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp b/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp
deleted file mode 100644
index e57b2c8..0000000
--- a/src/plugins/gamepads/evdev/qevdevgamepadbackend.cpp
+++ /dev/null
@@ -1,553 +0,0 @@
-/****************************************************************************
-**
-** 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 <QtCore/qglobal.h>
-QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // GCC warnings don't make sense, so disable
-
-#include "qevdevgamepadbackend_p.h"
-#include <QtCore/QSocketNotifier>
-#include <QtCore/QLoggingCategory>
-#include <QtCore/QVariant>
-#include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h>
-#include <QtCore/private/qcore_unix_p.h>
-#include <linux/input.h>
-
-#include <cmath>
-
-QT_BEGIN_NAMESPACE
-
-Q_LOGGING_CATEGORY(lcEGB, "qt.gamepad")
-
-#ifndef BTN_TRIGGER_HAPPY1
-# define BTN_TRIGGER_HAPPY1 0x2c0
-#endif
-#ifndef BTN_TRIGGER_HAPPY2
-# define BTN_TRIGGER_HAPPY2 0x2c1
-#endif
-#ifndef BTN_TRIGGER_HAPPY3
-# define BTN_TRIGGER_HAPPY3 0x2c2
-#endif
-#ifndef BTN_TRIGGER_HAPPY4
-# define BTN_TRIGGER_HAPPY4 0x2c3
-#endif
-
-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 = std::abs(absInfo.flat / double(maxValue - minValue));
- }
-}
-
-void QEvdevGamepadDevice::EvdevAxisInfo::restoreSavedData(int fd, int abs, const QVariantMap &value)
-{
- gamepadAxis = QGamepadManager::GamepadAxis(value[QLatin1String("axis")].toInt());
- gamepadMinButton = QGamepadManager::GamepadButton(value[QLatin1String("minButton")].toInt());
- gamepadMaxButton = QGamepadManager::GamepadButton(value[QLatin1String("maxButton")].toInt());
- setAbsInfo(fd, abs);
-}
-
-QVariantMap QEvdevGamepadDevice::EvdevAxisInfo::dataToSave() const
-{
- QVariantMap data;
- data[QLatin1String("axis")] = gamepadAxis;
- data[QLatin1String("minButton")] = gamepadMinButton;
- data[QLatin1String("maxButton")] = gamepadMaxButton;
- return data;
-}
-
-QEvdevGamepadBackend::QEvdevGamepadBackend()
-{
-}
-
-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) {
- const QStringList devices = m_discovery->scanConnectedDevices();
- for (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);
-}
-
-QEvdevGamepadDevice *QEvdevGamepadBackend::device(int deviceId)
-{
- for (QEvdevGamepadDevice *device : qAsConst(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()
-{
- 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_fd(-1),
- m_productId(0),
- m_needsConfigure(true),
- m_notifier(0),
- m_configureButton(QGamepadManager::ButtonInvalid),
- m_configureAxis(QGamepadManager::AxisInvalid)
-{
- openDevice(dev);
-}
-
-QEvdevGamepadDevice::~QEvdevGamepadDevice()
-{
- if (m_fd != -1)
- QT_CLOSE(m_fd);
-
- if (m_productId)
- emit m_backend->gamepadRemoved(m_productId);
-}
-
-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)
-{
- 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;
- }
-
- input_id id;
- if (ioctl(m_fd, EVIOCGID, &id) >= 0) {
- m_productId = id.product;
-
- QVariant settings = m_backend->readSettings(m_productId);
- if (!settings.isNull()) {
- m_needsConfigure = false;
- QVariantMap data = settings.toMap()[QLatin1String("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());
- }
-
- data = settings.toMap()[QLatin1String("buttons")].toMap();
- for (QVariantMap::const_iterator it = data.begin(); it != data.end(); ++it)
- m_buttonsMap[it.key().toInt()] = QGamepadManager::GamepadButton(it.value().toInt());
- }
-
- emit m_backend->gamepadAdded(m_productId);
-
- // same as libevdev::libevdev_set_fd() in libevdev.c
- char buffer[256];
- memset(buffer, 0, sizeof(buffer));
- if (ioctl(m_fd, EVIOCGNAME(sizeof(buffer) - 1), buffer) >= 0)
- emit m_backend->gamepadNamed(m_productId, QString::fromUtf8(buffer));
-
- } else {
- QT_CLOSE(m_fd);
- m_fd = -1;
- return false;
- }
-
- if (m_needsConfigure)
- resetConfiguration();
-
- qCDebug(lcEGB) << "Axis limits:" << m_axisMap;
-
- return true;
-}
-
-QDebug operator<<(QDebug dbg, const QEvdevGamepadDevice::EvdevAxisInfo &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;
- }
- }
- }
-}
-
-void QEvdevGamepadDevice::saveData()
-{
- 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[QLatin1String("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[QLatin1String("buttons")] = data;
-
- m_backend->saveSettings(m_productId, settings);
-}
-
-void QEvdevGamepadDevice::processInputEvent(input_event *e)
-{
- if (e->type == EV_KEY) {
- QGamepadManager::GamepadButton btn = QGamepadManager::ButtonInvalid;
- ButtonsMap::const_iterator it = m_buttonsMap.find(e->code);
- if (it != m_buttonsMap.end())
- btn = it.value();
-
- 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_productId);
- return;
- }
-
- 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_productId, but);
- }
-
- it = m_buttonsMap.find(e->code);
- if (it != m_buttonsMap.end())
- btn = it.value();
-
- if (btn != QGamepadManager::ButtonInvalid) {
- if (pressed)
- emit m_backend->gamepadButtonPressed(m_productId, btn, 1.0);
- else
- emit m_backend->gamepadButtonReleased(m_productId, btn);
- }
- } else if (e->type == EV_ABS) {
- if (m_configureAxis != QGamepadManager::AxisInvalid) {
- EvdevAxisInfo inf(m_fd, e->code, -32768, 32767, m_configureAxis);
- if (std::abs(inf.normalized(e->value)) == 1) {
- 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_productId, axis);
- } else {
- return;
- }
- }
-
- 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;
- }
-
- if (save) {
- QGamepadManager::GamepadButton but = m_configureButton;
- m_configureButton = QGamepadManager::ButtonInvalid;
- if (but == QGamepadManager::ButtonL2 || but == QGamepadManager::ButtonR2)
- m_axisMap.insert(e->code, axisInfo);
- saveData();
- emit m_backend->buttonConfigured(m_productId, but);
- }
- }
-
- it = m_axisMap.find(e->code);
- if (it == m_axisMap.end())
- return;
-
- EvdevAxisInfo &info = it.value();
-
- double val = info.normalized(e->value);
-
- if (info.gamepadAxis != QGamepadManager::AxisInvalid)
- emit m_backend->gamepadAxisMoved(m_productId, info.gamepadAxis, val);
-
- if (info.gamepadMaxButton == info.gamepadMinButton &&
- info.gamepadMaxButton != QGamepadManager::ButtonInvalid) {
- if (val)
- emit m_backend->gamepadButtonPressed(m_productId, info.gamepadMaxButton, std::abs(val));
- else
- emit m_backend->gamepadButtonReleased(m_productId, info.gamepadMaxButton);
- } else {
- if (info.gamepadMaxButton != QGamepadManager::ButtonInvalid
- && val == 1.0) {
- info.gamepadLastButton = info.gamepadMaxButton;
- emit m_backend->gamepadButtonPressed(m_productId, info.gamepadMaxButton, val);
- } else if (info.gamepadMinButton != QGamepadManager::ButtonInvalid
- && val == -1.0) {
- info.gamepadLastButton = info.gamepadMinButton;
- emit m_backend->gamepadButtonPressed(m_productId, info.gamepadMinButton, -val);
- } else if (!val && info.gamepadLastButton != QGamepadManager::ButtonInvalid) {
- QGamepadManager::GamepadButton but = info.gamepadLastButton;
- info.gamepadLastButton = QGamepadManager::ButtonInvalid;
- emit m_backend->gamepadButtonReleased(m_productId, but);
- }
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h b/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h
deleted file mode 100644
index 35e2fbd..0000000
--- a/src/plugins/gamepads/evdev/qevdevgamepadbackend_p.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/****************************************************************************
-**
-** 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 QEVDEVGAMEPADBACKEND_P_H
-#define QEVDEVGAMEPADBACKEND_P_H
-
-#include <QtGamepad/QGamepadManager>
-#include <QtGamepad/private/qgamepadbackend_p.h>
-#include <QtCore/QHash>
-#include <QtCore/QList>
-
-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; }
- int deviceId() const { return m_productId; }
- void resetConfiguration();
- bool isConfigurationNeeded();
- bool configureButton(QGamepadManager::GamepadButton button);
- bool configureAxis(QGamepadManager::GamepadAxis axis);
- bool setCancelConfigureButton(QGamepadManager::GamepadButton button);
-
-private Q_SLOTS:
- void readData();
-
-private:
- void saveData();
- void processInputEvent(input_event *e);
- bool openDevice(const QByteArray &dev);
-
- QByteArray m_dev;
- QEvdevGamepadBackend *m_backend;
- int m_fd;
- int m_productId;
- bool m_needsConfigure;
- QSocketNotifier *m_notifier;
- 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 override;
- 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;
- };
- 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::EvdevAxisInfo &axisInfo);
-
-class QEvdevGamepadBackend : public QGamepadBackend
-{
- Q_OBJECT
-
-public:
- QEvdevGamepadBackend();
- bool start() override;
- void stop() override;
- void resetConfiguration(int deviceId) override;
- bool isConfigurationNeeded(int deviceId) override;
- bool configureButton(int deviceId, QGamepadManager::GamepadButton button) override;
- bool configureAxis(int deviceId, QGamepadManager::GamepadAxis axis) override;
- bool setCancelConfigureButton(int deviceId, QGamepadManager::GamepadButton button) override;
-
-private slots:
- void handleAddedDevice(const QString &device);
- void handleRemovedDevice(const QString &device);
-
-private:
- QEvdevGamepadDevice *newDevice(const QByteArray &device);
- QEvdevGamepadDevice *device(int deviceId);
-
- QDeviceDiscovery *m_discovery;
- QList<QEvdevGamepadDevice *> m_devices;
-};
-
-QT_END_NAMESPACE
-
-#endif // QEVDEVGAMEPADBACKEND_P_H
diff --git a/src/plugins/gamepads/gamepads.pro b/src/plugins/gamepads/gamepads.pro
deleted file mode 100644
index 2151b79..0000000
--- a/src/plugins/gamepads/gamepads.pro
+++ /dev/null
@@ -1,7 +0,0 @@
-TEMPLATE = subdirs
-QT_FOR_CONFIG += gui-private gamepad-private
-qtConfig(sdl2): SUBDIRS += sdl2
-!android: qtConfig(evdev): SUBDIRS += evdev
-win32: SUBDIRS += xinput
-darwin: !watchos: SUBDIRS += darwin
-android: !android-embedded: SUBDIRS += android
diff --git a/src/plugins/gamepads/sdl2/main.cpp b/src/plugins/gamepads/sdl2/main.cpp
deleted file mode 100644
index 30b0ceb..0000000
--- a/src/plugins/gamepads/sdl2/main.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Gamepad module
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtGamepad/private/qgamepadbackendfactory_p.h>
-#include <QtGamepad/private/qgamepadbackendplugin_p.h>
-
-#include "qsdlgamepadbackend_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QSdl2GamepadBackendPlugin : public QGamepadBackendPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "sdl2.json")
-public:
- QGamepadBackend *create(const QString &key, const QStringList &paramList) override;
-};
-
-QGamepadBackend *QSdl2GamepadBackendPlugin::create(const QString &key, const QStringList &paramList) {
- Q_UNUSED(key);
- Q_UNUSED(paramList);
-
- return new QSdlGamepadBackend();
-}
-
-QT_END_NAMESPACE
-
-#include "main.moc"
diff --git a/src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp b/src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp
deleted file mode 100644
index 6a7cee6..0000000
--- a/src/plugins/gamepads/sdl2/qsdlgamepadbackend.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-/****************************************************************************
-**
-** 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>
-// Reset bool redefinition from SDL header
-#undef bool
-
-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;
- addController(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);
- for (int i = 0; i < SDL_NumJoysticks() ; i++)
- addController(i);
-
- 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;
- }
-}
-
-void QSdlGamepadBackend::addController(int index)
-{
- char GUID[100];
- SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(index), GUID, 100);
- if (!SDL_IsGameController(index))
- return;
-
- SDL_GameController *controller = SDL_GameControllerOpen(index);
- if (controller) {
- m_indexForController.insert(index, controller);
-
- SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller);
-
- int instanceID = SDL_JoystickInstanceID(joystick);
- m_instanceIdForIndex.insert(instanceID, index);
-
- const char *name = SDL_JoystickName(joystick);
-
- //qDebug() << "Controller " << index << " added with instanceId: " << instanceID;
- emit gamepadAdded(index);
-
- if (name)
- emit gamepadNamed(index, QString::fromUtf8(name));
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h b/src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h
deleted file mode 100644
index dfd7176..0000000
--- a/src/plugins/gamepads/sdl2/qsdlgamepadbackend_p.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************************
-**
-** 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 QSDLGAMEPADBACKEND_P_H
-#define QSDLGAMEPADBACKEND_P_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 = nullptr);
- ~QSdlGamepadBackend();
-
-private Q_SLOTS:
- void pumpSdlEventLoop();
-
-protected:
- bool start() override;
- void stop() override;
-
-private:
- QGamepadManager::GamepadButton translateButton(int button);
- void addController(int index);
- QTimer m_eventLoopTimer;
- QMap<int, SDL_GameController*> m_indexForController;
- QMap<int, int> m_instanceIdForIndex;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSDLGAMEPADBACKEND_P_H
diff --git a/src/plugins/gamepads/sdl2/sdl2.json b/src/plugins/gamepads/sdl2/sdl2.json
deleted file mode 100644
index d2bd585..0000000
--- a/src/plugins/gamepads/sdl2/sdl2.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": [ "sdl2" ]
-}
diff --git a/src/plugins/gamepads/sdl2/sdl2.pro b/src/plugins/gamepads/sdl2/sdl2.pro
deleted file mode 100644
index 91715a3..0000000
--- a/src/plugins/gamepads/sdl2/sdl2.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-TARGET = sdl2gamepad
-QT += gamepad gamepad-private
-
-PLUGIN_TYPE = gamepads
-PLUGIN_CLASS_NAME = QSdl2GamepadBackendPlugin
-load(qt_plugin)
-
-QMAKE_USE += sdl2
-
-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
deleted file mode 100644
index c157cfd..0000000
--- a/src/plugins/gamepads/xinput/main.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the Qt Gamepad module
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPLv3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtGamepad/private/qgamepadbackendfactory_p.h>
-#include <QtGamepad/private/qgamepadbackendplugin_p.h>
-
-#include "qxinputgamepadbackend_p.h"
-
-QT_BEGIN_NAMESPACE
-
-class QXInputGamepadBackendPlugin : public QGamepadBackendPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QtGamepadBackendFactoryInterface_iid FILE "xinput.json")
-public:
- QGamepadBackend *create(const QString &key, const QStringList &paramList) override;
-};
-
-QGamepadBackend *QXInputGamepadBackendPlugin::create(const QString &key, const QStringList &paramList)
-{
- Q_UNUSED(key);
- Q_UNUSED(paramList);
-
- return new QXInputGamepadBackend;
-}
-
-QT_END_NAMESPACE
-
-#include "main.moc"
diff --git a/src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp b/src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp
deleted file mode 100644
index 4d042c9..0000000
--- a/src/plugins/gamepads/xinput/qxinputgamepadbackend.cpp
+++ /dev/null
@@ -1,291 +0,0 @@
-/****************************************************************************
-**
-** 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() 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 (uint 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
deleted file mode 100644
index 28f5b23..0000000
--- a/src/plugins/gamepads/xinput/qxinputgamepadbackend_p.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** 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 QXINPUTGAMEPADBACKEND_P_H
-#define QXINPUTGAMEPADBACKEND_P_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() override;
- void stop() override;
-
-private:
- QXInputThread *m_thread;
- QLibrary m_lib;
-};
-
-QT_END_NAMESPACE
-
-#endif // QXINPUTGAMEPADBACKEND_P_H
diff --git a/src/plugins/gamepads/xinput/xinput.json b/src/plugins/gamepads/xinput/xinput.json
deleted file mode 100644
index f8e3fb2..0000000
--- a/src/plugins/gamepads/xinput/xinput.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "Keys": [ "xinput" ]
-}
diff --git a/src/plugins/gamepads/xinput/xinput.pro b/src/plugins/gamepads/xinput/xinput.pro
deleted file mode 100644
index 5dac854..0000000
--- a/src/plugins/gamepads/xinput/xinput.pro
+++ /dev/null
@@ -1,14 +0,0 @@
-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
diff --git a/src/plugins/joystickinputs/CMakeLists.txt b/src/plugins/joystickinputs/CMakeLists.txt
new file mode 100644
index 0000000..1641f8f
--- /dev/null
+++ b/src/plugins/joystickinputs/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# for each platform, add a subdirectory with the platform name
+if(ANDROID)
+ add_subdirectory(android)
+endif()
+if(MACOS)
+ add_subdirectory(macos)
+endif()
+if(IOS)
+ add_subdirectory(ios)
+endif()
+if(WIN32)
+ add_subdirectory(windows)
+endif()
+if(LINUX)
+ add_subdirectory(linux)
+endif()
+
+#add_subdirectory(macos)
+#add_subdirectory(ios)
+#add_subdirectory(windows)
diff --git a/src/plugins/joystickinputs/android/CMakeLists.txt b/src/plugins/joystickinputs/android/CMakeLists.txt
new file mode 100644
index 0000000..d55aae7
--- /dev/null
+++ b/src/plugins/joystickinputs/android/CMakeLists.txt
@@ -0,0 +1,43 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set(java_sources
+ jar/src/org/qtproject/qt/android/universalinput/QtJoystickInputHandler.java
+ jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java
+)
+
+qt_internal_add_jar(Qt${QtUniversalInput_VERSION_MAJOR}AndroidJoystickInput
+ INCLUDE_JARS ${QT_ANDROID_JAR}
+ SOURCES ${java_sources}
+ OUTPUT_DIR "${QT_BUILD_DIR}/jar"
+)
+
+qt_path_join(destination ${INSTALL_DATADIR} "jar")
+
+install_jar(Qt${QtUniversalInput_VERSION_MAJOR}AndroidJoystickInput
+ DESTINATION ${destination}
+ COMPONENT Devel
+)
+
+
+qt_internal_add_plugin(AndroidJoystickInputPlugin
+ OUTPUT_NAME androidjoystickinput
+ PLUGIN_TYPE joystickinputs
+ DEFAULT_IF ANDROID
+ SOURCES
+ androidjoystickinput.cpp androidjoystickinput.h
+ androidjoystickinputplugin.cpp androidjoystickinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+
+)
+
+set_property(
+ TARGET
+ AndroidJoystickInputPlugin
+ APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
+ jar/Qt${QtUniversalInput_VERSION_MAJOR}AndroidJoystickInput.jar
+)
diff --git a/src/plugins/gamepads/android/src/android.json b/src/plugins/joystickinputs/android/android.json
index 6843bd3..6843bd3 100644
--- a/src/plugins/gamepads/android/src/android.json
+++ b/src/plugins/joystickinputs/android/android.json
diff --git a/src/plugins/joystickinputs/android/androidjoystickinput.cpp b/src/plugins/joystickinputs/android/androidjoystickinput.cpp
new file mode 100644
index 0000000..861c1d8
--- /dev/null
+++ b/src/plugins/joystickinputs/android/androidjoystickinput.cpp
@@ -0,0 +1,142 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/android/java_godot_lib_jni.cpp" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "androidjoystickinput.h"
+#include <QtCore/qnativeinterface.h>
+#include <QtUniversalInput/QUniversalInput>
+
+
+
+using namespace QtJniTypes;
+
+Q_DECLARE_JNI_CLASS(KeyEvent, "android/view/KeyEvent")
+Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")
+
+static void joyConnectionChanged(JNIEnv *, jclass, int deviceId, bool connected, jstring name)
+{
+ QUniversalInput::instance()->updateJoyConnection(deviceId, connected, QJniObject(name).toString());
+}
+Q_DECLARE_JNI_NATIVE_METHOD(joyConnectionChanged)
+
+static void joyButton(JNIEnv *, jclass, int deviceId, int button, bool pressed)
+{
+ qDebug() << "joyButton" << deviceId << button << pressed;
+ QUniversalInput::instance()->joyButton(deviceId, JoyButton(button), pressed);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(joyButton)
+
+static void joyAxis(JNIEnv *, jclass, int deviceId, int axis, float value)
+{
+ QUniversalInput::instance()->joyAxis(deviceId, JoyAxis(axis), value);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(joyAxis)
+
+static void joyHat(JNIEnv *, jclass, int deviceId, int hatX, int hatY)
+{
+ HatMask hat = HatMask::Center;
+ if (hatX != 0) {
+ if (hatX < 0)
+ hat |= HatMask::Left;
+ else
+ hat |= HatMask::Right;
+ }
+ if (hatY != 0) {
+ if (hatY < 0)
+ hat |= HatMask::Up;
+ else
+ hat |= HatMask::Down;
+ }
+
+ QUniversalInput::instance()->joyHat(deviceId, hat);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(joyHat)
+
+QT_BEGIN_NAMESPACE
+
+const char keyEventClass[] = "android/view/KeyEvent";
+inline int keyField(const char *field)
+{
+ return QJniObject::getStaticField<jint>(keyEventClass, field);
+}
+
+
+static void initJNI()
+{
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ qWarning() << "initJNI called in Qt Joystick Input Handler";
+
+ if (!QtJoystickInputHandler::registerNativeMethods({
+ Q_JNI_NATIVE_METHOD(joyConnectionChanged),
+ Q_JNI_NATIVE_METHOD(joyButton),
+ Q_JNI_NATIVE_METHOD(joyAxis),
+ Q_JNI_NATIVE_METHOD(joyHat)}))
+ qCritical("Failed to register native methods for QtJoystickInputHandler");
+}
+
+AndroidJoystickInput::AndroidJoystickInput()
+{
+ initJNI();
+ m_qtJoystickInputHandler = QtJoystickInputHandler(QtAndroidPrivate::activity());
+ QtAndroidPrivate::registerGenericMotionEventListener(this);
+ QtAndroidPrivate::registerKeyEventListener(this);
+ start();
+}
+
+AndroidJoystickInput::~AndroidJoystickInput()
+{
+ stop();
+ QtAndroidPrivate::unregisterGenericMotionEventListener(this);
+ QtAndroidPrivate::unregisterKeyEventListener(this);
+}
+
+bool AndroidJoystickInput::handleKeyEvent(jobject event)
+{
+ static int ACTION_DOWN = keyField("ACTION_DOWN");
+ static int ACTION_UP = keyField("ACTION_UP");
+ QJniObject ev(event);
+
+ // Pass the event to the QtJoystickInputHandler Java class.
+ if (m_qtJoystickInputHandler.isValid()) {
+ const int keyCode = ev.callMethod<jint>("getKeyCode", "()I");
+ const int action = ev.callMethod<jint>("getAction", "()I");
+
+ if (action == ACTION_UP)
+ return m_qtJoystickInputHandler.callMethod<bool>("onKeyUp", keyCode, KeyEvent(event));
+ else if (action == ACTION_DOWN)
+ return m_qtJoystickInputHandler.callMethod<bool>("onKeyDown", keyCode, KeyEvent(event));
+ }
+
+ return false;
+}
+
+bool AndroidJoystickInput::handleGenericMotionEvent(jobject event)
+{
+ if (m_qtJoystickInputHandler.isValid())
+ return m_qtJoystickInputHandler.callMethod<bool>("onGenericMotionEvent", MotionEvent(event));
+
+ return false;
+}
+
+void AndroidJoystickInput::start()
+{
+ if (QtAndroidPrivate::androidSdkVersion() >= 16)
+ m_qtJoystickInputHandler.callMethod<void>("register", jlong(this));
+}
+
+void AndroidJoystickInput::stop()
+{
+ if (QtAndroidPrivate::androidSdkVersion() >= 16)
+ m_qtJoystickInputHandler.callMethod<void>("unregister");
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/android/androidjoystickinput.h b/src/plugins/joystickinputs/android/androidjoystickinput.h
new file mode 100644
index 0000000..d79fb43
--- /dev/null
+++ b/src/plugins/joystickinputs/android/androidjoystickinput.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef ANDROIDJOYSTICKINPUT_H
+#define ANDROIDJOYSTICKINPUT_H
+
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+#include <QtCore/QMutex>
+#include <QtCore/QJNIObject.h>
+#include <QtCore/qjnitypes.h>
+#include <QtCore/private/qjnihelpers_p.h>
+
+Q_DECLARE_JNI_CLASS(QtJoystickInputHandler, "org/qtproject/qt/android/universalinput/QtJoystickInputHandler");
+
+QT_BEGIN_NAMESPACE
+
+class AndroidJoystickInput : public QJoystickInput, public QtAndroidPrivate::GenericMotionEventListener, public QtAndroidPrivate::KeyEventListener
+{
+ Q_OBJECT
+public:
+ AndroidJoystickInput();
+ ~AndroidJoystickInput();
+
+ // KeyEventListener interface
+ bool handleKeyEvent(jobject event) override;
+
+ // GenericMotionEventListener interface
+ bool handleGenericMotionEvent(jobject event) override;
+
+private:
+ void start();
+ void stop();
+
+ QtJniTypes::QtJoystickInputHandler m_qtJoystickInputHandler;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDJOYSTICKINPUT_H
diff --git a/src/plugins/joystickinputs/android/androidjoystickinputplugin.cpp b/src/plugins/joystickinputs/android/androidjoystickinputplugin.cpp
new file mode 100644
index 0000000..7fc2440
--- /dev/null
+++ b/src/plugins/joystickinputs/android/androidjoystickinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "androidjoystickinputplugin.h"
+#include "androidjoystickinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QJoystickInput *AndroidJoystickInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("android"))
+ return new AndroidJoystickInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/android/androidjoystickinputplugin.h b/src/plugins/joystickinputs/android/androidjoystickinputplugin.h
new file mode 100644
index 0000000..a39c440
--- /dev/null
+++ b/src/plugins/joystickinputs/android/androidjoystickinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef ANDROIDJOYSTICKINPUTPLUGIN_H
+#define ANDROIDJOYSTICKINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qjoystickinputplugin_p.h>
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class AndroidJoystickInputPlugin : public QJoystickInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "android.json")
+
+public:
+ QJoystickInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // ANDROIDJOYSTICKINPUTPLUGIN_H
diff --git a/src/plugins/joystickinputs/android/jar/AndroidManifest.xml b/src/plugins/joystickinputs/android/jar/AndroidManifest.xml
new file mode 100644
index 0000000..4636623
--- /dev/null
+++ b/src/plugins/joystickinputs/android/jar/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.qt.android.universalinput"
+ android:versionCode="1"
+ android:versionName="1.0" >
+ <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
+</manifest>
diff --git a/src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java b/src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java
new file mode 100644
index 0000000..29a39d6
--- /dev/null
+++ b/src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystick.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/android/java/lib/src/org/godotengine/godot/input/Joystick.java" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+package org.qtproject.qt.android.universalinput;
+
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Joystick {
+ int device_id;
+ String name;
+ List<Integer> axes = new ArrayList<>();
+ protected boolean hasAxisHat = false;
+ /*
+ * Keep track of values so we can prevent flooding the engine with useless events.
+ */
+ protected final SparseArray<Float> axesValues = new SparseArray<>(4);
+ protected int hatX;
+ protected int hatY;
+}
diff --git a/src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystickInputHandler.java b/src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystickInputHandler.java
new file mode 100644
index 0000000..b57f34b
--- /dev/null
+++ b/src/plugins/joystickinputs/android/jar/src/org/qtproject/qt/android/universalinput/QtJoystickInputHandler.java
@@ -0,0 +1,308 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+package org.qtproject.qt.android.universalinput;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.Build;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class QtJoystickInputHandler implements InputManager.InputDeviceListener {
+
+ private final SparseIntArray m_joystickIds = new SparseIntArray(4);
+ private final SparseArray<Joystick> m_joystickDevices = new SparseArray<>(4);
+ private final InputManager m_inputManager;
+ Activity m_activity;
+ private long m_nativePtr = 0;
+
+ public QtJoystickInputHandler(Activity activity) {
+ m_activity = activity;
+ m_inputManager = (InputManager)activity.getSystemService(Context.INPUT_SERVICE);
+ }
+
+ public void register(long nativePtr)
+ {
+ synchronized (this) {
+ if (m_inputManager != null) {
+ m_nativePtr = nativePtr;
+ m_activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ m_inputManager.registerInputDeviceListener(QtJoystickInputHandler.this, null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ initInputDevices();
+ }
+ }
+ }
+
+ public void unregister()
+ {
+ synchronized (this) {
+ if (m_inputManager != null) {
+ m_nativePtr = 0;
+ m_inputManager.unregisterInputDeviceListener(this);
+ }
+ }
+ }
+
+ private boolean isKeyEventGameDevice(int source) {
+ if (source == (InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD))
+ return false;
+ return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
+ }
+
+ public boolean onKeyUp(final int keyCode, KeyEvent event) {
+ int source = event.getSource();
+ if (isKeyEventGameDevice(source)) {
+ // Check if the device exists
+ final int deviceId = event.getDeviceId();
+ if (m_joystickIds.indexOfKey(deviceId) >= 0) {
+ final int button = getButtonValue(keyCode);
+ final int joyId = m_joystickIds.get(deviceId);
+ joyButton(joyId, button, false);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean onKeyDown(final int keyCode, KeyEvent event) {
+ int source = event.getSource();
+ final int deviceId = event.getDeviceId();
+ // Check if source is a game device and that the device is a registered gamepad
+ if (isKeyEventGameDevice(source)) {
+ if (event.getRepeatCount() > 0) // ignore key echo
+ return true;
+
+ if (m_joystickIds.indexOfKey(deviceId) >= 0) {
+ final int button = getButtonValue(keyCode);
+ final int joyId = m_joystickIds.get(deviceId);
+ joyButton(joyId, button, true);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
+ // Check if the device exists
+ final int deviceId = event.getDeviceId();
+ if (m_joystickIds.indexOfKey(deviceId) >= 0) {
+ final int joyId = m_joystickIds.get(deviceId);
+ Joystick joystick = m_joystickDevices.get(deviceId);
+ if (joystick == null) {
+ return true;
+ }
+
+ for (int i = 0; i < joystick.axes.size(); i++) {
+ final int axis = joystick.axes.get(i);
+ final float value = event.getAxisValue(axis);
+ /*
+ As all axes are polled for each event, only fire an axis event if the value has actually changed.
+ Prevents flooding Godot with repeated events.
+ */
+ if (joystick.axesValues.indexOfKey(axis) < 0 || (float)joystick.axesValues.get(axis) != value) {
+ // save value to prevent repeats
+ joystick.axesValues.put(axis, value);
+ joyAxis(joyId, i, value);
+ }
+ }
+
+ if (joystick.hasAxisHat) {
+ final int hatX = Math.round(event.getAxisValue(MotionEvent.AXIS_HAT_X));
+ final int hatY = Math.round(event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ if (joystick.hatX != hatX || joystick.hatY != hatY) {
+ joystick.hatX = hatX;
+ joystick.hatY = hatY;
+ joyHat(joyId, hatX, hatY);
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void initInputDevices() {
+ int[] deviceIds = m_inputManager.getInputDeviceIds();
+ for (int deviceId : deviceIds) {
+ InputDevice device = m_inputManager.getInputDevice(deviceId);
+ onInputDeviceAdded(deviceId);
+ }
+ }
+
+ private int assignJoystickIdNumber(int deviceId) {
+ int joyId = 0;
+ while (m_joystickIds.indexOfValue(joyId) >= 0) {
+ joyId++;
+ }
+ m_joystickIds.put(deviceId, joyId);
+ return joyId;
+ }
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ // Check if the device has not been already added
+
+ if (m_joystickIds.indexOfKey(deviceId) >= 0) {
+ return;
+ }
+
+ InputDevice device = m_inputManager.getInputDevice(deviceId);
+ //device can be null if deviceId is not found
+ if (device == null) {
+ return;
+ }
+
+ int sources = device.getSources();
+
+ // Device may not be a joystick or gamepad
+ if ((sources & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD &&
+ (sources & InputDevice.SOURCE_JOYSTICK) != InputDevice.SOURCE_JOYSTICK) {
+ return;
+ }
+
+ // Assign first available number. Re-use numbers where possible.
+ final int id = assignJoystickIdNumber(deviceId);
+
+ final Joystick joystick = new Joystick();
+ joystick.device_id = deviceId;
+ joystick.name = device.getName();
+
+ Set<Integer> already = new HashSet<>();
+ for (InputDevice.MotionRange range : device.getMotionRanges()) {
+ boolean isJoystick = range.isFromSource(InputDevice.SOURCE_JOYSTICK);
+ boolean isGamepad = range.isFromSource(InputDevice.SOURCE_GAMEPAD);
+ if (!isJoystick && !isGamepad) {
+ continue;
+ }
+ final int axis = range.getAxis();
+ if (axis == MotionEvent.AXIS_HAT_X || axis == MotionEvent.AXIS_HAT_Y) {
+ joystick.hasAxisHat = true;
+ } else {
+ if (!already.contains(axis)) {
+ already.add(axis);
+ joystick.axes.add(axis);
+ }
+ }
+ }
+ Collections.sort(joystick.axes);
+ m_joystickDevices.put(deviceId, joystick);
+
+ joyConnectionChanged(id, true, joystick.name);
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ // Check if the device has not been already removed
+ if (m_joystickIds.indexOfKey(deviceId) < 0) {
+ return;
+ }
+ final int joyId = m_joystickIds.get(deviceId);
+ m_joystickIds.delete(deviceId);
+ m_joystickDevices.delete(deviceId);
+ joyConnectionChanged(joyId, false, "");
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ onInputDeviceRemoved(deviceId);
+ onInputDeviceAdded(deviceId);
+ }
+
+ public static int getButtonValue(int keyCode) {
+ int button;
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BUTTON_A:
+ button = 0;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_B:
+ button = 1;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_X:
+ button = 2;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_Y:
+ button = 3;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_L1:
+ button = 9;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_L2:
+ button = 15;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_R1:
+ button = 10;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_R2:
+ button = 16;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_SELECT:
+ button = 4;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_START:
+ button = 6;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_THUMBL:
+ button = 7;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_THUMBR:
+ button = 8;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ button = 11;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ button = 12;
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ button = 13;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ button = 14;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_C:
+ button = 17;
+ break;
+ case KeyEvent.KEYCODE_BUTTON_Z:
+ button = 18;
+ break;
+
+ default:
+ button = keyCode - KeyEvent.KEYCODE_BUTTON_1 + 20;
+ break;
+ }
+ return button;
+ }
+
+ private static native void joyConnectionChanged(int id, boolean connected, String name);
+ private static native void joyButton(int id, int button, boolean pressed);
+ private static native void joyAxis(int id, int axis, float value);
+ private static native void joyHat(int id, int hatX, int hatY);
+}
diff --git a/src/plugins/joystickinputs/ios/CMakeLists.txt b/src/plugins/joystickinputs/ios/CMakeLists.txt
new file mode 100644
index 0000000..6ee106c
--- /dev/null
+++ b/src/plugins/joystickinputs/ios/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(IosJoystickInputPlugin
+ OUTPUT_NAME iosjoystickinput
+ PLUGIN_TYPE joystickinputs
+ SOURCES
+ iosjoystickinput.mm iosjoystickinput.h
+ iosjoystickinputplugin.cpp iosjoystickinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+ ${FWFoundation}
+ ${FWGameController}
+)
diff --git a/src/plugins/joystickinputs/ios/ios.json b/src/plugins/joystickinputs/ios/ios.json
new file mode 100644
index 0000000..f0b766d
--- /dev/null
+++ b/src/plugins/joystickinputs/ios/ios.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "ios" ]
+}
diff --git a/src/plugins/joystickinputs/ios/iosjoystickinput.h b/src/plugins/joystickinputs/ios/iosjoystickinput.h
new file mode 100644
index 0000000..e1cf1c0
--- /dev/null
+++ b/src/plugins/joystickinputs/ios/iosjoystickinput.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef IOSJOYSTICKINPUT_H
+#define IOSJOYSTICKINPUT_H
+
+#include <QtCore/QObject>
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+Q_FORWARD_DECLARE_OBJC_CLASS(JoypadIOSObserver);
+
+QT_BEGIN_NAMESPACE
+
+class IosJoystickInput : public QJoystickInput{
+public:
+ IosJoystickInput();
+ ~IosJoystickInput();
+
+ void startProcessing();
+
+private:
+ JoypadIOSObserver *m_observer;
+};
+
+QT_END_NAMESPACE
+
+#endif // IOSJOYSTICKINPUT_H
diff --git a/src/plugins/joystickinputs/ios/iosjoystickinput.mm b/src/plugins/joystickinputs/ios/iosjoystickinput.mm
new file mode 100644
index 0000000..218f195
--- /dev/null
+++ b/src/plugins/joystickinputs/ios/iosjoystickinput.mm
@@ -0,0 +1,318 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/ios/joypad_ios.mm" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "iosjoystickinput.h"
+
+#import <GameController/GameController.h>
+
+@interface JoypadIOSObserver : NSObject
+
+- (void)startObserving;
+- (void)startProcessing;
+- (void)finishObserving;
+
+@property(assign, nonatomic) BOOL isObserving;
+@property(assign, nonatomic) BOOL isProcessing;
+@property(strong, nonatomic) NSMutableDictionary *connectedJoypads;
+@property(strong, nonatomic) NSMutableArray *joypadsQueue;
+
+@end
+
+@implementation JoypadIOSObserver
+
+- (instancetype)init
+{
+ self = [super init];
+
+ if (self) {
+ self.isObserving = NO;
+ self.isProcessing = NO;
+ }
+
+ return self;
+}
+
+- (void)startProcessing
+{
+ self.isProcessing = YES;
+
+ for (GCController *controller in self.joypadsQueue) {
+ [self addiOSJoypad:controller];
+ }
+
+ [self.joypadsQueue removeAllObjects];
+}
+
+- (void)startObserving
+{
+ if (self.isObserving)
+ return;
+
+ self.isObserving = YES;
+
+ self.connectedJoypads = [NSMutableDictionary dictionary];
+ self.joypadsQueue = [NSMutableArray array];
+
+ // get told when controllers connect, this will be called right away for
+ // already connected controllers
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasConnected:)
+ name:GCControllerDidConnectNotification
+ object:nil];
+
+ // get told when controllers disconnect
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasDisconnected:)
+ name:GCControllerDidDisconnectNotification
+ object:nil];
+}
+
+- (void)finishObserving
+{
+ if (self.isObserving)
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ self.isObserving = NO;
+ self.isProcessing = NO;
+
+ self.connectedJoypads = nil;
+ self.joypadsQueue = nil;
+}
+
+- (void)dealloc
+{
+ [self finishObserving];
+ [super dealloc];
+}
+
+- (int)getJoyIdForController:(GCController *)controller
+{
+ NSArray *keys = [self.connectedJoypads allKeysForObject:controller];
+
+ for (NSNumber *key in keys) {
+ int joy_id = [key intValue];
+ return joy_id;
+ }
+
+ return -1;
+}
+
+- (void)addiOSJoypad:(GCController *)controller
+{
+ // get a new id for our controller
+ int joy_id = QUniversalInput::instance()->getUnusedJoyId();
+
+ if (joy_id == -1) {
+ printf("Couldn't retrieve new joy id\n");
+ return;
+ }
+
+ // assign our player index
+ if (controller.playerIndex == GCControllerPlayerIndexUnset)
+ controller.playerIndex = [self getFreePlayerIndex];
+
+
+ // Report the new controller
+ QUniversalInput::instance()->updateJoyConnection(joy_id, true, QString::fromUtf8([controller.vendorName UTF8String]));
+
+ // add it to our dictionary, this will retain our controllers
+ [self.connectedJoypads setObject:controller forKey:[NSNumber numberWithInt:joy_id]];
+
+ // set our input handler
+ [self setControllerInputHandler:controller];
+}
+
+- (void)controllerWasConnected:(NSNotification *)notification
+{
+ // get our controller
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller) {
+ printf("Couldn't retrieve new controller\n");
+ return;
+ }
+
+ if ([[self.connectedJoypads allKeysForObject:controller] count] > 0) {
+ printf("Controller is already registered\n");
+ } else if (!self.isProcessing) {
+ [self.joypadsQueue addObject:controller];
+ } else {
+ [self addiOSJoypad:controller];
+ }
+}
+
+- (void)controllerWasDisconnected:(NSNotification *)notification
+{
+ // find our joystick, there should be only one in our dictionary
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller)
+ return;
+
+ NSArray *keys = [self.connectedJoypads allKeysForObject:controller];
+ for (NSNumber *key in keys) {
+ // Report this joystick is no longer there
+ int joy_id = [key intValue];
+ QUniversalInput::instance()->updateJoyConnection(joy_id, false, "");
+
+ // and remove it from our dictionary
+ [self.connectedJoypads removeObjectForKey:key];
+ }
+}
+
+- (GCControllerPlayerIndex)getFreePlayerIndex {
+ bool have_player_1 = false;
+ bool have_player_2 = false;
+ bool have_player_3 = false;
+ bool have_player_4 = false;
+
+ if (self.connectedJoypads == nil) {
+ NSArray *keys = [self.connectedJoypads allKeys];
+ for (NSNumber *key in keys) {
+ GCController *controller = [self.connectedJoypads objectForKey:key];
+ if (controller.playerIndex == GCControllerPlayerIndex1) {
+ have_player_1 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex2) {
+ have_player_2 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex3) {
+ have_player_3 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex4) {
+ have_player_4 = true;
+ }
+ }
+ }
+
+ if (!have_player_1) {
+ return GCControllerPlayerIndex1;
+ } else if (!have_player_2) {
+ return GCControllerPlayerIndex2;
+ } else if (!have_player_3) {
+ return GCControllerPlayerIndex3;
+ } else if (!have_player_4) {
+ return GCControllerPlayerIndex4;
+ } else {
+ return GCControllerPlayerIndexUnset;
+ }
+}
+
+- (void)setControllerInputHandler:(GCController *)controller
+{
+ // Hook in the callback handler for the correct gamepad profile.
+ // This is a bit of a weird design choice on Apples part.
+ // You need to select the most capable gamepad profile for the
+ // gamepad attached.
+ if (controller.extendedGamepad != nil) {
+ // The extended gamepad profile has all the input you could possibly find on
+ // a gamepad but will only be active if your gamepad actually has all of
+ // these...
+
+ controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
+
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonB) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::B,
+ gamepad.buttonB.isPressed);
+ } else if (element == gamepad.buttonX) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.buttonY) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::Y,
+ gamepad.buttonY.isPressed);
+ } else if (element == gamepad.leftShoulder) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::LeftShoulder,
+ gamepad.leftShoulder.isPressed);
+ } else if (element == gamepad.rightShoulder) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::RightShoulder,
+ gamepad.rightShoulder.isPressed);
+ } else if (element == gamepad.dpad) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadUp,
+ gamepad.dpad.up.isPressed);
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadDown,
+ gamepad.dpad.down.isPressed);
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadLeft,
+ gamepad.dpad.left.isPressed);
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadRight,
+ gamepad.dpad.right.isPressed);
+ }
+
+ if (element == gamepad.leftThumbstick) {
+ float value = gamepad.leftThumbstick.xAxis.value;
+ QUniversalInput::instance()->joyAxis(joy_id, JoyAxis::LeftX, value);
+ value = -gamepad.leftThumbstick.yAxis.value;
+ QUniversalInput::instance()->joyAxis(joy_id, JoyAxis::LeftY, value);
+ } else if (element == gamepad.rightThumbstick) {
+ float value = gamepad.rightThumbstick.xAxis.value;
+ QUniversalInput::instance()->joyAxis(joy_id, JoyAxis::RightX, value);
+ value = -gamepad.rightThumbstick.yAxis.value;
+ QUniversalInput::instance()->joyAxis(joy_id, JoyAxis::RightY, value);
+ } else if (element == gamepad.leftTrigger) {
+ float value = gamepad.leftTrigger.value;
+ QUniversalInput::instance()->joyAxis(joy_id, JoyAxis::TriggerLeft, value);
+ } else if (element == gamepad.rightTrigger) {
+ float value = gamepad.rightTrigger.value;
+ QUniversalInput::instance()->joyAxis(joy_id, JoyAxis::TriggerRight, value);
+ }
+ };
+ } else if (controller.microGamepad != nil) {
+ // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad
+
+ controller.microGamepad.valueChangedHandler = ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
+
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonX) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.dpad) {
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadUp,
+ gamepad.dpad.up.isPressed);
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadDown,
+ gamepad.dpad.down.isPressed);
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadLeft, gamepad.dpad.left.isPressed);
+ QUniversalInput::instance()->joyButton(joy_id, JoyButton::DpadRight, gamepad.dpad.right.isPressed);
+ }
+ };
+ }
+}
+
+@end
+
+QT_BEGIN_NAMESPACE
+
+IosJoystickInput::IosJoystickInput()
+{
+ m_observer = [[JoypadIOSObserver alloc] init];
+ [m_observer startObserving];
+ startProcessing();
+}
+
+IosJoystickInput::~IosJoystickInput()
+{
+ if (m_observer) {
+ [m_observer finishObserving];
+ m_observer = nil;
+ }
+}
+
+void IosJoystickInput::startProcessing()
+{
+ if (m_observer)
+ [m_observer startProcessing];
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/ios/iosjoystickinputplugin.cpp b/src/plugins/joystickinputs/ios/iosjoystickinputplugin.cpp
new file mode 100644
index 0000000..7952f87
--- /dev/null
+++ b/src/plugins/joystickinputs/ios/iosjoystickinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "iosjoystickinputplugin.h"
+#include "iosjoystickinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QJoystickInput *IosJoystickInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("ios"))
+ return new IosJoystickInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/ios/iosjoystickinputplugin.h b/src/plugins/joystickinputs/ios/iosjoystickinputplugin.h
new file mode 100644
index 0000000..c4e04f0
--- /dev/null
+++ b/src/plugins/joystickinputs/ios/iosjoystickinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef IOSJOYSTICKINPUTPLUGIN_H
+#define IOSJOYSTICKINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qjoystickinputplugin_p.h>
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class IosJoystickInputPlugin : public QJoystickInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "ios.json")
+
+public:
+ QJoystickInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // IOSJOYSTICKINPUTPLUGIN_H
diff --git a/src/plugins/joystickinputs/linux/CMakeLists.txt b/src/plugins/joystickinputs/linux/CMakeLists.txt
new file mode 100644
index 0000000..7bc01e9
--- /dev/null
+++ b/src/plugins/joystickinputs/linux/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(LinuxJoystickInputPlugin
+ OUTPUT_NAME linuxjoystickinput
+ PLUGIN_TYPE joystickinputs
+ SOURCES
+ linuxjoystickinput.cpp linuxjoystickinput.h
+ linuxjoystickinputplugin.cpp linuxjoystickinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+ # todo: do the dependency check in cmake
+ udev
+)
diff --git a/src/plugins/joystickinputs/linux/linux.json b/src/plugins/joystickinputs/linux/linux.json
new file mode 100644
index 0000000..8165d39
--- /dev/null
+++ b/src/plugins/joystickinputs/linux/linux.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "linux" ]
+}
diff --git a/src/plugins/joystickinputs/linux/linuxjoystickinput.cpp b/src/plugins/joystickinputs/linux/linuxjoystickinput.cpp
new file mode 100644
index 0000000..321c62f
--- /dev/null
+++ b/src/plugins/joystickinputs/linux/linuxjoystickinput.cpp
@@ -0,0 +1,453 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/linuxbsd/joypad_linux.cpp" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "linuxjoystickinput.h"
+
+#include <QtCore/QDir>
+
+#include <libudev.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+
+using namespace Qt::Literals::StringLiterals;
+
+QT_BEGIN_NAMESPACE
+
+// GODOT begin
+#define LONG_BITS (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1) / LONG_BITS) + 1)
+#define test_bit(nr, addr) (((1UL << ((nr) % LONG_BITS)) & ((addr)[(nr) / LONG_BITS])) != 0)
+
+static QString ignore_str = u"js"_s;
+// GODOT end
+
+LinuxJoystickInput::LinuxJoystickInput()
+ : m_udev(nullptr)
+{
+ m_udev = udev_new();
+ if (!m_udev) {
+ qWarning() << "Could not initialize udev";
+ m_udev = nullptr; // ensure udev is nullptr
+ }
+
+ probeJoypads();
+
+ m_elapsedTimer.start();
+
+ // ### Replace with Thread later
+ startTimer(1);
+}
+
+LinuxJoystickInput::~LinuxJoystickInput()
+{
+ if (m_udev)
+ udev_unref(m_udev);
+
+ m_udev = nullptr;
+}
+
+void LinuxJoystickInput::probeJoypads()
+{
+ if (!m_udev) {
+ qWarning() << "Could not probe joypads, udev is not initialized";
+ return;
+ }
+
+ struct udev_enumerate *enumerate = udev_enumerate_new(m_udev);
+ udev_enumerate_add_match_subsystem(enumerate, "input");
+
+ udev_enumerate_scan_devices(enumerate);
+ struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
+ struct udev_list_entry *entry = nullptr;
+ udev_list_entry_foreach(entry, devices) {
+ const char *path = udev_list_entry_get_name(entry);
+ udev_device *dev = udev_device_new_from_syspath(m_udev, path);
+ // QString action = udev_device_get_action(dev);
+ const char *devnode = udev_device_get_devnode(dev);
+
+ if (devnode) {
+ QString devnode_str = devnode;
+
+ // check if exists
+ if (std::find(m_attached_devices.begin(), m_attached_devices.end(), devnode_str) != m_attached_devices.end())
+ continue;
+
+ if (!devnode_str.contains(ignore_str))
+ setupJoypadObject(devnode_str);
+ }
+
+ udev_device_unref(dev);
+ }
+ udev_enumerate_unref(enumerate);
+}
+
+static inline uint16_t BSWAP16(uint16_t x)
+{
+ return (x >> 8) | (x << 8);
+}
+
+static inline QString _hex_str(QChar c)
+{
+ QString str;
+ str = QString("%1").arg(c.unicode(), 0, 16);
+ return str;
+}
+
+void LinuxJoystickInput::setupJoypadObject(const QString &device)
+{
+ auto input = QUniversalInput::instance();
+ int id = input->getUnusedJoyId();
+ if (id == -1) {
+ qWarning() << "Could not find unused joypad";
+ return;
+ }
+
+ // GODOT begin ; dont know what is godot and what is ours now
+ // tries to open the device to check if it's a joystick
+ int fd = open(device.toUtf8().constData(), O_RDWR | O_NONBLOCK);
+ if (fd == -1) {
+ // qWarning() << "Could not open device" << device << "for reading";
+ // race condition? the only one that can be opened is xbox360 controller
+ return;
+ }
+
+ unsigned long evbit[NBITS(EV_MAX)] = { 0 };
+ unsigned long keybit[NBITS(MAX_KEY)] = { 0 };
+ unsigned long absbit[NBITS(MAX_ABS)] = { 0 };
+
+ // add to attached devices so we don't try to open it again
+ m_attached_devices.push_back(device);
+
+ if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
+ (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
+ (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
+ close(fd);
+ return;
+ }
+
+ // Check if the device supports basic gamepad events
+ bool has_abs_left = (test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit));
+ bool has_abs_right = (test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit));
+ if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && (has_abs_left || has_abs_right))) {
+ close(fd);
+ return;
+ }
+
+ char namebuf[128];
+ QString name = "";
+ if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) >= 0)
+ name = namebuf;
+
+
+ input_id inpid;
+ if (ioctl(fd, EVIOCGID, &inpid) < 0) {
+ close(fd);
+ return;
+ }
+
+ // reset gamepad
+ m_joypads[id] = gamepad();
+ auto& joy = m_joypads[id];
+ joy.fd = fd;
+ joy.devpath = QString(device);
+ joy.attached = true;
+ joy.id = id;
+ joy.vibrating = false;
+
+ setupJoypadProperties(&joy);
+
+ char uid[64];
+ sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
+ if (inpid.vendor && inpid.product && inpid.version) {
+ uint16_t vendor = BSWAP16(inpid.vendor);
+ uint16_t product = BSWAP16(inpid.product);
+ uint16_t version = BSWAP16(inpid.version);
+
+ sprintf(uid + QString(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
+ input->updateJoyConnection(id, true, "Udev Joypad", uid);
+ } else {
+ QString uidname = uid;
+ int uidlen = std::min((int)name.length(), 11);
+ for (int i = 0; i < uidlen; i++) {
+ uidname = uidname + _hex_str(name[i]);
+ }
+ uidname += "00";
+ input->updateJoyConnection(id, true, "Udev Joypad", uidname);
+ }
+
+ // GODOT end
+}
+
+void LinuxJoystickInput::setupJoypadProperties(gamepad* joy)
+{
+ // GODOT begin
+ unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
+ unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
+
+ int num_buttons = 0;
+ int num_axes = 0;
+
+ if ((ioctl(joy->fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
+ (ioctl(joy->fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
+ return;
+ }
+ for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i)
+ if (test_bit(i, keybit))
+ joy->key_map[i] = num_buttons++;
+
+ for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i)
+ if (test_bit(i, keybit))
+ joy->key_map[i] = num_buttons++;
+
+ for (int i = 0; i < ABS_MISC; ++i) {
+ /* Skip hats */
+ if (i == ABS_HAT0X) {
+ i = ABS_HAT3Y;
+ continue;
+ }
+ if (test_bit(i, absbit)) {
+ joy->joy_axis[i] = num_axes++;
+ joy->abs_info[i] = new input_absinfo;
+ if (ioctl(joy->fd, EVIOCGABS(i), joy->abs_info[i]) < 0) {
+ delete joy->abs_info[i];
+ joy->abs_info[i] = nullptr;
+ }
+ }
+ }
+
+ joy->force_feedback = false;
+ unsigned long ffbit[NBITS(FF_CNT)];
+ if (ioctl(joy->fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) != -1)
+ if (test_bit(FF_RUMBLE, ffbit))
+ joy->force_feedback = true;
+
+
+ // GODOT end
+}
+
+void LinuxJoystickInput::closeJoypads()
+{
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ gamepad &joypad = m_joypads[i];
+ closeJoypad(joypad, i);
+ }
+}
+
+void LinuxJoystickInput::closeJoypad(const char *p_devpath)
+{
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ gamepad &joypad = m_joypads[i];
+ if (m_joypads[i].devpath == p_devpath)
+ closeJoypad(joypad, i);
+ }
+}
+
+void LinuxJoystickInput::closeJoypad(gamepad &p_joypad, int p_id)
+{
+ auto input = QUniversalInput::instance();
+
+ if (p_joypad.fd != -1) {
+ close(p_joypad.fd);
+ p_joypad.fd = -1;
+ m_attached_devices.erase(std::find(m_attached_devices.begin(), m_attached_devices.end(), p_joypad.devpath));
+ input->updateJoyConnection(p_id, false, "");
+ }
+}
+
+static inline float axisCorrect(int value, int min, int max)
+{
+ return 2.0f * (value - min) / (max - min) - 1.0f;
+}
+
+void LinuxJoystickInput::processJoypads()
+{
+ auto input = QUniversalInput::instance();
+
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ gamepad& joy = m_joypads[i];
+ if (!joy.attached)
+ continue;
+
+ // get joypad events
+ input_event event;
+ std::vector<input_event> events;
+ while (read(joy.fd, &event, sizeof(event)) > 0) {
+ events.push_back(event);
+ }
+
+ if (errno != EAGAIN) {
+ closeJoypad(joy, i);
+ continue;
+ }
+
+ // GODOT begin
+
+ for (const auto& event : events) {
+ // event may be tainted and out of MAX_KEY range, which will cause
+ // joy.key_map[event.code] to crash
+ if (event.code >= MAX_KEY) {
+ qDebug() << "Joypad event code out of range:" << event.code;
+ continue;
+ }
+
+ switch (event.type) {
+ case EV_KEY:
+ input->joyButton(joy.id, (JoyButton)joy.key_map[event.code], event.value);
+ break;
+
+ case EV_ABS:
+ switch (event.code) {
+ case ABS_HAT0X:
+ if (event.value != 0) {
+ if (event.value < 0) {
+ joy.dpad = HatMask::Left;
+ } else {
+ joy.dpad = HatMask::Right;
+ }
+ } else {
+ joy.dpad = HatMask::Center;
+ }
+ input->joyHat(i, joy.dpad);
+ break;
+
+ case ABS_HAT0Y:
+ if (event.value != 0) {
+ if (event.value < 0) {
+ joy.dpad = HatMask::Up;
+ } else {
+ joy.dpad = HatMask::Down;
+ }
+ } else {
+ joy.dpad = HatMask::Center;
+ }
+ input->joyHat(i, joy.dpad);
+ break;
+
+ default:
+ if (event.code >= MAX_ABS) {
+ continue;
+ }
+ if (joy.abs_info[event.code]) {
+ // using the min/max values from the device
+ auto min = joy.abs_info[event.code]->minimum;
+ auto max = joy.abs_info[event.code]->maximum;
+
+ float value = event.value;
+ value = axisCorrect(value, min, max);
+
+ JoyAxis axis = JoyAxis::Invalid;
+
+ switch (event.code) {
+ case ABS_X:
+ axis = JoyAxis::LeftX;
+ break;
+ case ABS_Y:
+ axis = JoyAxis::LeftY;
+ break;
+ case ABS_RX:
+ axis = JoyAxis::RightX;
+ break;
+ case ABS_RY:
+ axis = JoyAxis::RightY;
+ break;
+ case ABS_Z:
+ axis = JoyAxis::TriggerLeft;
+ break;
+ case ABS_RZ:
+ axis = JoyAxis::TriggerRight;
+ break;
+ }
+
+ input->joyAxis(joy.id, axis, value);
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ if (joy.force_feedback) {
+ uint64_t timestamp = input->getJoyVibrationTimestamp(joy.id);
+ float duration = input->getJoyVibrationDuration(joy.id) * 1000.f;
+ QVector2D strength = input->getJoyVibrationStrength(joy.id);
+ uint64_t currentTimestamp = QDateTime::currentMSecsSinceEpoch();
+ if (currentTimestamp - timestamp <= duration) {
+ if (!joy.vibrating)
+ joypadVibrationStart(joy, strength.x(), strength.y(), duration, timestamp);
+ } else if (joy.vibrating) {
+ joypadVibrationStop(joy, 0);
+ }
+ }
+
+ // GODOT end
+ }
+}
+
+void LinuxJoystickInput::joypadVibrationStart(gamepad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp)
+{
+ // GODOT start
+
+ if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f)
+ return;
+
+ if (p_joypad.ff_effect_id != -1)
+ joypadVibrationStop(p_joypad, p_timestamp);
+
+ struct ff_effect effect;
+ effect.type = FF_RUMBLE;
+ effect.id = -1;
+ effect.u.rumble.weak_magnitude = floor(p_weak_magnitude * (float)0xffff);
+ effect.u.rumble.strong_magnitude = floor(p_strong_magnitude * (float)0xffff);
+ effect.replay.length = floor(p_duration);
+ effect.replay.delay = 0;
+
+ if (ioctl(p_joypad.fd, EVIOCSFF, &effect) < 0)
+ return;
+
+ struct input_event play;
+ play.type = EV_FF;
+ play.code = effect.id;
+ play.value = 1;
+ if (write(p_joypad.fd, (const void *)&play, sizeof(play)) == -1)
+ qWarning() << "Couldn't write to Joypad device.";
+
+ p_joypad.ff_effect_id = effect.id;
+
+ // GODOT end
+}
+
+void LinuxJoystickInput::joypadVibrationStop(gamepad &p_joypad, uint64_t p_timestamp)
+{
+ Q_UNUSED(p_timestamp);
+
+ // GODOT start
+ if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_joypad.ff_effect_id == -1)
+ return;
+
+ if (ioctl(p_joypad.fd, EVIOCRMFF, p_joypad.ff_effect_id) < 0) {
+ return;
+ }
+
+ p_joypad.ff_effect_id = -1;
+ p_joypad.vibrating = false;
+ // GODOT end
+}
+
+void LinuxJoystickInput::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event);
+ probeJoypads();
+ processJoypads();
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/linux/linuxjoystickinput.h b/src/plugins/joystickinputs/linux/linuxjoystickinput.h
new file mode 100644
index 0000000..a6cc278
--- /dev/null
+++ b/src/plugins/joystickinputs/linux/linuxjoystickinput.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/linuxbsd/joypad_linux.h" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#ifndef LINUXJOYSTICKINPUT_H
+#define LINUXJOYSTICKINPUT_H
+
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+#include <QtCore/QElapsedTimer>
+#include <QtCore/QLibrary>
+#include <QtCore/QThread>
+
+#include <vector>
+
+// for JoypadEvent
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+#include <quniversalinput.h>
+
+struct udev;
+struct input_absinfo;
+
+QT_BEGIN_NAMESPACE
+
+class LinuxJoystickInput : public QJoystickInput
+{
+ Q_OBJECT
+public:
+ LinuxJoystickInput();
+ ~LinuxJoystickInput();
+
+ void probeJoypads();
+ void processJoypads();
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ enum {
+ JOYPADS_MAX = 16,
+ JOY_AXIS_COUNT = 6,
+ MIN_JOY_AXIS = 10,
+ MAX_JOY_AXIS = 32768,
+ MAX_JOY_BUTTONS = 128,
+ KEY_EVENT_BUFFER_SIZE = 512,
+ MAX_TRIGGER = 1023, // was 255, but xbox one controller max is 1023
+
+ // from godot linux_joystick.h
+ MAX_ABS = 63,
+ MAX_KEY = 767, // Hack because <linux/input.h> can't be included here
+ };
+
+ struct gamepad {
+ int id;
+ bool attached;
+ bool confirmed;
+ int key_map[MAX_KEY];
+ int joy_axis[JOY_AXIS_COUNT];
+
+ HatMask dpad;
+
+ int fd;
+ QString devpath;
+
+ input_absinfo *abs_info[MAX_ABS] = {};
+
+ bool force_feedback;
+ int ff_effect_id;
+ bool vibrating = false;
+
+ gamepad() {
+ id = -1;
+ attached = false;
+ confirmed = false;
+ fd = -1;
+ }
+ };
+
+ void setupJoypadObject(const QString& name);
+ void setupJoypadProperties(gamepad* joy);
+ void closeJoypads();
+ void closeJoypad(const char *p_devpath);
+ void closeJoypad(gamepad &p_joypad, int p_id);
+
+ void joypadVibrationStart(gamepad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
+ void joypadVibrationStop(gamepad &p_joypad, uint64_t p_timestamp);
+
+ struct udev *m_udev = nullptr;
+ gamepad m_joypads[JOYPADS_MAX]; // joypad joystick gamestick tomatoe potatoe
+ std::vector<QString> m_attached_devices;
+ QElapsedTimer m_elapsedTimer;
+};
+
+QT_END_NAMESPACE
+
+#endif // LINUXJOYSTICKINPUT_H
diff --git a/src/plugins/joystickinputs/linux/linuxjoystickinputplugin.cpp b/src/plugins/joystickinputs/linux/linuxjoystickinputplugin.cpp
new file mode 100644
index 0000000..c234a26
--- /dev/null
+++ b/src/plugins/joystickinputs/linux/linuxjoystickinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "linuxjoystickinputplugin.h"
+#include "linuxjoystickinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QJoystickInput *LinuxJoystickInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("linux"))
+ return new LinuxJoystickInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/linux/linuxjoystickinputplugin.h b/src/plugins/joystickinputs/linux/linuxjoystickinputplugin.h
new file mode 100644
index 0000000..7260fa8
--- /dev/null
+++ b/src/plugins/joystickinputs/linux/linuxjoystickinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef LINUXJOYSTICKINPUTPLUGIN_H
+#define LINUXJOYSTICKINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qjoystickinputplugin_p.h>
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class LinuxJoystickInputPlugin : public QJoystickInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "linux.json")
+
+public:
+ QJoystickInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // LINUXJOYSTICKINPUTPLUGIN_H
diff --git a/src/plugins/joystickinputs/macos/CMakeLists.txt b/src/plugins/joystickinputs/macos/CMakeLists.txt
new file mode 100644
index 0000000..0e82dfd
--- /dev/null
+++ b/src/plugins/joystickinputs/macos/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_find_apple_system_framework(FWForceFeedback ForceFeedback)
+
+qt_internal_add_plugin(MacOSJoystickInputPlugin
+ OUTPUT_NAME macosjoystickinput
+ PLUGIN_TYPE joystickinputs
+ SOURCES
+ macosjoystickinput.mm macosjoystickinput.h
+ macosjoystickinputplugin.cpp macosjoystickinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+ ${FWIOKit}
+ ${FWForceFeedback}
+)
diff --git a/src/plugins/joystickinputs/macos/macos.json b/src/plugins/joystickinputs/macos/macos.json
new file mode 100644
index 0000000..a3f3787
--- /dev/null
+++ b/src/plugins/joystickinputs/macos/macos.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "macos" ]
+}
diff --git a/src/plugins/joystickinputs/macos/macosjoystickinput.h b/src/plugins/joystickinputs/macos/macosjoystickinput.h
new file mode 100644
index 0000000..a313d4e
--- /dev/null
+++ b/src/plugins/joystickinputs/macos/macosjoystickinput.h
@@ -0,0 +1,104 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#ifndef MACOSJOYSTICKINPUT_H
+#define MACOSJOYSTICKINPUT_H
+
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+#include <QtCore/QVector>
+
+#import <ForceFeedback/ForceFeedback.h>
+#import <ForceFeedback/ForceFeedbackConstants.h>
+#import <IOKit/hid/IOHIDLib.h>
+#import <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
+
+QT_BEGIN_NAMESPACE
+
+struct RecElement {
+ IOHIDElementRef ref;
+ IOHIDElementCookie cookie;
+
+ uint32_t usage = 0;
+
+ int min = 0;
+ int max = 0;
+
+ struct Comparator {
+ bool operator()(const RecElement p_a, const RecElement p_b) const { return p_a.usage < p_b.usage; }
+ };
+};
+
+struct Joypad {
+ IOHIDDeviceRef deviceRef = nullptr;
+
+ QVector<RecElement> axisElements;
+ QVector<RecElement> buttonElements;
+ QVector<RecElement> hatElements;
+
+ int id = 0;
+ bool offsetHat = false;
+
+ io_service_t forceFeedbackService = 0;
+ FFCONSTANTFORCE forceFeedbackConstantForce;
+ FFDeviceObjectReference forceFeedbackDevice = nullptr;
+ FFEffectObjectReference forcefeedbackObject = nullptr;
+ quint64 forceFeedbackTimestamp = 0;
+ LONG *forceFeedbackDirections = nullptr;
+ FFEFFECT forceFeedbackEffect;
+ DWORD *forceFeedbackAxes = nullptr;
+
+ void add_hid_elements(CFArrayRef p_array);
+ void add_hid_element(IOHIDElementRef p_element);
+
+ bool has_element(IOHIDElementCookie p_cookie, QVector<RecElement> *p_list) const;
+ bool config_force_feedback(io_service_t p_service);
+ bool check_ff_features();
+
+ int get_hid_element_state(RecElement *p_element) const;
+
+ void free();
+ Joypad();
+};
+
+class MacOsJoystickInput : public QJoystickInput
+{
+ Q_OBJECT
+public:
+ MacOsJoystickInput();
+ ~MacOsJoystickInput();
+
+ void processJoypads();
+
+ void deviceAdded(IOReturn p_res, IOHIDDeviceRef p_devic);
+ void deviceRemoved(IOReturn p_res, IOHIDDeviceRef p_device);
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ bool have_device(IOHIDDeviceRef p_device) const;
+ bool configure_joypad(IOHIDDeviceRef p_device_ref, Joypad *p_joy);
+
+ int get_joy_index(int p_id) const;
+ int get_joy_ref(IOHIDDeviceRef p_device) const;
+
+ void poll_joypads() const;
+ void config_hid_manager(CFArrayRef p_matching_array) const;
+
+ void joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp);
+ void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
+
+
+ IOHIDManagerRef m_hidManager;
+ QVector<Joypad> m_deviceList;
+};
+
+QT_END_NAMESPACE
+
+#endif // MACOSJOYSTICKINPUT_H
diff --git a/src/plugins/joystickinputs/macos/macosjoystickinput.mm b/src/plugins/joystickinputs/macos/macosjoystickinput.mm
new file mode 100644
index 0000000..0a4767a
--- /dev/null
+++ b/src/plugins/joystickinputs/macos/macosjoystickinput.mm
@@ -0,0 +1,623 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.cpp" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "macosjoystickinput.h"
+#include <QtUniversalInput/QUniversalInput>
+#include <QtEndian>
+
+QT_BEGIN_NAMESPACE
+
+#define JOYPAD_LOOP_RUN_MODE CFSTR("QtUniversalInput")
+
+static MacOsJoystickInput *self = nullptr;
+
+Joypad::Joypad()
+{
+ forceFeedbackConstantForce.lMagnitude = 10000;
+ forceFeedbackEffect.dwDuration = 0;
+ forceFeedbackEffect.dwSamplePeriod = 0;
+ forceFeedbackEffect.dwGain = 10000;
+ forceFeedbackEffect.dwFlags = FFEFF_OBJECTOFFSETS;
+ forceFeedbackEffect.dwTriggerButton = FFEB_NOTRIGGER;
+ forceFeedbackEffect.dwStartDelay = 0;
+ forceFeedbackEffect.dwTriggerRepeatInterval = 0;
+ forceFeedbackEffect.lpEnvelope = nullptr;
+ forceFeedbackEffect.cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
+ forceFeedbackEffect.lpvTypeSpecificParams = &forceFeedbackConstantForce;
+ forceFeedbackEffect.dwSize = sizeof(forceFeedbackEffect);
+}
+
+void Joypad::free()
+{
+ if (deviceRef)
+ IOHIDDeviceUnscheduleFromRunLoop(deviceRef, CFRunLoopGetCurrent(), JOYPAD_LOOP_RUN_MODE);
+
+ if (forceFeedbackDevice) {
+ FFDeviceReleaseEffect(forceFeedbackDevice, forcefeedbackObject);
+ FFReleaseDevice(forceFeedbackDevice);
+ forceFeedbackDevice = nullptr;
+ ::free(forceFeedbackAxes);
+ ::free(forceFeedbackDirections);
+ }
+}
+
+bool Joypad::has_element(IOHIDElementCookie p_cookie, QVector<RecElement> *p_list) const
+{
+ for (int i = 0; i < p_list->size(); i++)
+ if (p_cookie == p_list->at(i).cookie)
+ return true;
+
+ return false;
+}
+
+int Joypad::get_hid_element_state(RecElement *p_element) const
+{
+ int value = 0;
+ if (p_element && p_element->ref) {
+ IOHIDValueRef valueRef;
+ if (IOHIDDeviceGetValue(deviceRef, p_element->ref, &valueRef) == kIOReturnSuccess) {
+ value = SInt32(IOHIDValueGetIntegerValue(valueRef));
+
+ // Record min and max for auto calibration.
+ if (value < p_element->min)
+ p_element->min = value;
+
+ if (value > p_element->max)
+ p_element->max = value;
+ }
+ }
+ return value;
+}
+
+void Joypad::add_hid_element(IOHIDElementRef p_element)
+{
+ const CFTypeID elementTypeID = p_element ? CFGetTypeID(p_element) : 0;
+
+ if (p_element && (elementTypeID == IOHIDElementGetTypeID())) {
+ const IOHIDElementCookie cookie = IOHIDElementGetCookie(p_element);
+ const uint32_t usagePage = IOHIDElementGetUsagePage(p_element);
+ const uint32_t usage = IOHIDElementGetUsage(p_element);
+ QVector<RecElement> *list = nullptr;
+
+ switch (IOHIDElementGetType(p_element)) {
+ case kIOHIDElementTypeInput_Misc:
+ case kIOHIDElementTypeInput_Button:
+ case kIOHIDElementTypeInput_Axis: {
+ switch (usagePage) {
+ case kHIDPage_GenericDesktop:
+ switch (usage) {
+ case kHIDUsage_GD_X:
+ case kHIDUsage_GD_Y:
+ case kHIDUsage_GD_Z:
+ case kHIDUsage_GD_Rx:
+ case kHIDUsage_GD_Ry:
+ case kHIDUsage_GD_Rz:
+ case kHIDUsage_GD_Slider:
+ case kHIDUsage_GD_Dial:
+ case kHIDUsage_GD_Wheel:
+ if (!has_element(cookie, &axisElements))
+ list = &axisElements;
+ break;
+
+ case kHIDUsage_GD_Hatswitch:
+ if (!has_element(cookie, &hatElements))
+ list = &hatElements;
+ break;
+ case kHIDUsage_GD_DPadUp:
+ case kHIDUsage_GD_DPadDown:
+ case kHIDUsage_GD_DPadRight:
+ case kHIDUsage_GD_DPadLeft:
+ case kHIDUsage_GD_Start:
+ case kHIDUsage_GD_Select:
+ if (!has_element(cookie, &buttonElements))
+ list = &buttonElements;
+ break;
+ }
+ break;
+
+ case kHIDPage_Simulation:
+ switch (usage) {
+ case kHIDUsage_Sim_Rudder:
+ case kHIDUsage_Sim_Throttle:
+ case kHIDUsage_Sim_Accelerator:
+ case kHIDUsage_Sim_Brake:
+ if (!has_element(cookie, &axisElements))
+ list = &axisElements;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case kHIDPage_Button:
+ case kHIDPage_Consumer:
+ if (!has_element(cookie, &buttonElements))
+ list = &buttonElements;
+ break;
+
+ default:
+ break;
+ }
+ } break;
+
+ case kIOHIDElementTypeCollection: {
+ CFArrayRef array = IOHIDElementGetChildren(p_element);
+ if (array)
+ add_hid_elements(array);
+ } break;
+
+ default:
+ break;
+ }
+
+ if (list) { // Add to list.
+ RecElement element;
+
+ element.ref = p_element;
+ element.usage = usage;
+
+ element.min = (SInt32)IOHIDElementGetLogicalMin(p_element);
+ element.max = (SInt32)IOHIDElementGetLogicalMax(p_element);
+ element.cookie = IOHIDElementGetCookie(p_element);
+ list->push_back(element);
+ std::sort(list->begin(), list->end(), RecElement::Comparator());
+ // list->sort_custom<rec_element::Comparator>();
+ }
+ }
+}
+
+static void hid_element_added(const void *p_value, void *p_parameter)
+{
+ Joypad *joy = static_cast<Joypad *>(p_parameter);
+ joy->add_hid_element(IOHIDElementRef(p_value));
+}
+
+void Joypad::add_hid_elements(CFArrayRef p_array)
+{
+ CFRange range = { 0, CFArrayGetCount(p_array) };
+ CFArrayApplyFunction(p_array, range, hid_element_added, this);
+}
+
+static void joypad_removed_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
+{
+ Q_UNUSED(ctx);
+ Q_UNUSED(sender);
+ self->deviceRemoved(res, ioHIDDeviceObject);
+}
+
+static void joypad_added_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject)
+{
+ Q_UNUSED(ctx);
+ Q_UNUSED(sender);
+ self->deviceAdded(res, ioHIDDeviceObject);
+}
+
+static bool is_joypad(IOHIDDeviceRef p_device_ref)
+{
+ int usage_page = 0;
+ int usage = 0;
+ CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDPrimaryUsagePageKey));
+ if (refCF)
+ CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &usage_page);
+
+ if (usage_page != kHIDPage_GenericDesktop)
+ return false;
+
+ refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDPrimaryUsageKey));
+ if (refCF)
+ CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &usage);
+
+ if ((usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController))
+ return false;
+
+ return true;
+}
+
+void MacOsJoystickInput::deviceAdded(IOReturn p_res, IOHIDDeviceRef p_device)
+{
+ if (p_res != kIOReturnSuccess || have_device(p_device))
+ return;
+
+ Joypad new_joypad;
+ if (is_joypad(p_device)) {
+ configure_joypad(p_device, &new_joypad);
+ const io_service_t ioservice = IOHIDDeviceGetService(p_device);
+ if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK) && new_joypad.config_force_feedback(ioservice))
+ new_joypad.forceFeedbackService = ioservice;
+ m_deviceList.push_back(new_joypad);
+ }
+ IOHIDDeviceScheduleWithRunLoop(p_device, CFRunLoopGetCurrent(), JOYPAD_LOOP_RUN_MODE);
+}
+
+void MacOsJoystickInput::deviceRemoved(IOReturn p_res, IOHIDDeviceRef p_device)
+{
+ Q_UNUSED(p_res);
+ int device = get_joy_ref(p_device);
+ Q_ASSERT(device != -1);
+
+ auto input = QUniversalInput::instance();
+
+ input->updateJoyConnection(m_deviceList[device].id, false, "");
+ m_deviceList[device].free();
+ m_deviceList.removeAt(device);
+}
+
+static QByteArray _hex_str(uint8_t p_byte)
+{
+ static const char *dict = "0123456789abcdef";
+ char ret[3];
+ ret[2] = 0;
+
+ ret[0] = dict[p_byte >> 4];
+ ret[1] = dict[p_byte & 0xF];
+
+ return ret;
+}
+
+bool MacOsJoystickInput::configure_joypad(IOHIDDeviceRef p_device_ref, Joypad *p_joy)
+{
+ p_joy->deviceRef = p_device_ref;
+ // Get device name.
+ QByteArray name;
+ char c_name[256];
+ CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductKey));
+ if (!refCF)
+ refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDManufacturerKey));
+
+ if ((!refCF) || (!CFStringGetCString((CFStringRef)refCF, c_name, sizeof(c_name), kCFStringEncodingUTF8)))
+ name = "Unidentified Joypad";
+ else
+ name = c_name;
+
+ auto input = QUniversalInput::instance();
+
+ int id = input->getUnusedJoyId();
+ if (id == -1)
+ return false;
+
+ p_joy->id = id;
+ int vendor = 0;
+ refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVendorIDKey));
+ if (refCF)
+ CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &vendor);
+
+ int product_id = 0;
+ refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductIDKey));
+ if (refCF)
+ CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id);
+
+ int version = 0;
+ refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVersionNumberKey));
+ if (refCF)
+ CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &version);
+
+ if (vendor && product_id) {
+ uint32_t big3 = qToBigEndian(3);
+ uint32_t bigVendor = qToBigEndian(vendor);
+ uint32_t bigProductId = qToBigEndian(product_id);
+ uint32_t bigVersion = qToBigEndian(version);
+
+ QString uid = QString("%1%2%3%4").arg(big3, 8, 16, QChar('0')).arg(bigVendor, 8, 16, QChar('0')).arg(bigProductId, 8, 16, QChar('0')).arg(bigVersion, 8, 16, QChar('0'));
+ input->updateJoyConnection(id, true, name, uid);
+ } else {
+ // Bluetooth device.
+ QByteArray guid = "05000000";
+ for (int i = 0; i < 12; i++) {
+ if (i < name.size())
+ guid += _hex_str(name[i]);
+ else
+ guid += "00";
+ }
+ input->updateJoyConnection(id, true, name, guid);
+ }
+
+ CFArrayRef array = IOHIDDeviceCopyMatchingElements(p_device_ref, nullptr, kIOHIDOptionsTypeNone);
+ if (array) {
+ p_joy->add_hid_elements(array);
+ CFRelease(array);
+ }
+ // Xbox controller hat values start at 1 rather than 0.
+ p_joy->offsetHat = vendor == 0x45e &&
+ (product_id == 0x0b05 ||
+ product_id == 0x02e0 ||
+ product_id == 0x02fd ||
+ product_id == 0x0b13);
+
+ return true;
+}
+
+#define FF_ERR() \
+ { \
+ if (ret != FF_OK) { \
+ FFReleaseDevice(forceFeedbackDevice); \
+ forceFeedbackDevice = nullptr; \
+ return false; \
+ } \
+ }
+bool Joypad::config_force_feedback(io_service_t p_service)
+{
+ HRESULT ret = FFCreateDevice(p_service, &forceFeedbackDevice);
+
+ if (ret != FF_OK)
+ return false;
+
+ ret = FFDeviceSendForceFeedbackCommand(forceFeedbackDevice, FFSFFC_RESET);
+ FF_ERR();
+
+ ret = FFDeviceSendForceFeedbackCommand(forceFeedbackDevice, FFSFFC_SETACTUATORSON);
+ FF_ERR();
+
+ if (check_ff_features()) {
+ ret = FFDeviceCreateEffect(forceFeedbackDevice, kFFEffectType_ConstantForce_ID, &forceFeedbackEffect, &forcefeedbackObject);
+ FF_ERR();
+ return true;
+ }
+ FFReleaseDevice(forceFeedbackDevice);
+ forceFeedbackDevice = nullptr;
+ return false;
+}
+#undef FF_ERR
+
+#define TEST_FF(ff) (features.supportedEffects & (ff))
+bool Joypad::check_ff_features()
+{
+ FFCAPABILITIES features;
+ HRESULT ret = FFDeviceGetForceFeedbackCapabilities(forceFeedbackDevice, &features);
+ if (ret == FF_OK && (features.supportedEffects & FFCAP_ET_CONSTANTFORCE)) {
+ uint32_t val;
+ ret = FFDeviceGetForceFeedbackProperty(forceFeedbackDevice, FFPROP_FFGAIN, &val, sizeof(val));
+ if (ret != FF_OK) {
+ return false;
+ }
+ int num_axes = features.numFfAxes;
+ forceFeedbackAxes = (DWORD *)::malloc(sizeof(DWORD) * num_axes);
+ forceFeedbackDirections = (LONG *)::malloc(sizeof(LONG) * num_axes);
+
+ for (int i = 0; i < num_axes; i++) {
+ forceFeedbackAxes[i] = features.ffAxes[i];
+ forceFeedbackDirections[i] = 0;
+ }
+
+ forceFeedbackEffect.cAxes = num_axes;
+ forceFeedbackEffect.rgdwAxes = forceFeedbackAxes;
+ forceFeedbackEffect.rglDirection = forceFeedbackDirections;
+ return true;
+ }
+ return false;
+}
+
+static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat)
+{
+ int range = (p_max - p_min + 1);
+ int value = p_value - p_min;
+ HatMask hat_value = HatMask::Center;
+ if (range == 4)
+ value *= 2;
+
+ if (p_offset_hat)
+ value -= 1;
+
+ switch (value) {
+ case 0:
+ hat_value = HatMask::Up;
+ break;
+ case 1:
+ hat_value = (HatMask::Up | HatMask::Right);
+ break;
+ case 2:
+ hat_value = HatMask::Right;
+ break;
+ case 3:
+ hat_value = (HatMask::Down | HatMask::Right);
+ break;
+ case 4:
+ hat_value = HatMask::Down;
+ break;
+ case 5:
+ hat_value = (HatMask::Down | HatMask::Left);
+ break;
+ case 6:
+ hat_value = HatMask::Left;
+ break;
+ case 7:
+ hat_value = (HatMask::Up | HatMask::Left);
+ break;
+ default:
+ hat_value = HatMask::Center;
+ break;
+ }
+ return hat_value;
+}
+
+void MacOsJoystickInput::poll_joypads() const
+{
+ while (CFRunLoopRunInMode(JOYPAD_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
+ // No-op. Pending callbacks will fire.
+ }
+}
+
+static float axis_correct(int p_value, int p_min, int p_max)
+{
+ // Convert to a value between -1.0f and 1.0f.
+ return 2.0f * (p_value - p_min) / (p_max - p_min) - 1.0f;
+}
+
+void MacOsJoystickInput::processJoypads()
+{
+ auto input = QUniversalInput::instance();
+ poll_joypads();
+
+ for (auto &joy : m_deviceList) {
+ for (int j = 0; j < joy.axisElements.size(); j++) {
+ RecElement &elem = joy.axisElements[j];
+ int value = joy.get_hid_element_state(&elem);
+ input->joyAxis(joy.id, JoyAxis(j), axis_correct(value, elem.min, elem.max));
+ }
+ for (int j = 0; j < joy.buttonElements.size(); j++) {
+ int value = joy.get_hid_element_state(&joy.buttonElements[j]);
+ input->joyButton(joy.id, JoyButton(j), (value >= 1));
+ }
+ for (int j = 0; j < joy.hatElements.size(); j++) {
+ RecElement &elem = joy.hatElements[j];
+ int value = joy.get_hid_element_state(&elem);
+ HatMask hat_value = process_hat_value(elem.min, elem.max, value, joy.offsetHat);
+ input->joyHat(joy.id, hat_value);
+ }
+
+ if (joy.forceFeedbackService) {
+ uint64_t timestamp = input->getJoyVibrationTimestamp(joy.id);
+ if (timestamp > joy.forceFeedbackTimestamp) {
+ QVector2D strength = input->getJoyVibrationStrength(joy.id);
+ float duration = input->getJoyVibrationDuration(joy.id);
+ if (strength.x() == 0 && strength.y() == 0) {
+ joypad_vibration_stop(joy.id, timestamp);
+ } else {
+ float gain = qMax(strength.x(), strength.y());
+ joypad_vibration_start(joy.id, gain, duration, timestamp);
+ }
+ }
+ }
+
+ }
+}
+
+void MacOsJoystickInput::joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp)
+{
+ Joypad *joy = &m_deviceList[get_joy_index(p_id)];
+ joy->forceFeedbackTimestamp = p_timestamp;
+ joy->forceFeedbackEffect.dwDuration = p_duration * FF_SECONDS;
+ joy->forceFeedbackEffect.dwGain = p_magnitude * FF_FFNOMINALMAX;
+ FFEffectSetParameters(joy->forcefeedbackObject, &joy->forceFeedbackEffect, FFEP_DURATION | FFEP_GAIN);
+ FFEffectStart(joy->forcefeedbackObject, 1, 0);
+}
+
+void MacOsJoystickInput::joypad_vibration_stop(int p_id, uint64_t p_timestamp)
+{
+ Joypad *joy = &m_deviceList[get_joy_index(p_id)];
+ joy->forceFeedbackTimestamp = p_timestamp;
+ FFEffectStop(joy->forcefeedbackObject);
+}
+
+int MacOsJoystickInput::get_joy_index(int p_id) const
+{
+ for (int i = 0; i < m_deviceList.size(); i++)
+ if (m_deviceList[i].id == p_id)
+ return i;
+
+ return -1;
+}
+
+int MacOsJoystickInput::get_joy_ref(IOHIDDeviceRef p_device) const
+{
+ for (int i = 0; i < m_deviceList.size(); i++)
+ if (m_deviceList[i].deviceRef == p_device)
+ return i;
+
+ return -1;
+}
+
+bool MacOsJoystickInput::have_device(IOHIDDeviceRef p_device) const
+{
+ for (int i = 0; i < m_deviceList.size(); i++)
+ if (m_deviceList[i].deviceRef == p_device)
+ return true;
+
+ return false;
+}
+
+static CFDictionaryRef create_match_dictionary(const UInt32 page, const UInt32 usage, int *okay)
+{
+ CFDictionaryRef retval = nullptr;
+ CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
+ CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
+
+ if (pageNumRef && usageNumRef) {
+ const void *keys[2] = { (void *)CFSTR(kIOHIDDeviceUsagePageKey), (void *)CFSTR(kIOHIDDeviceUsageKey) };
+ const void *vals[2] = { (void *)pageNumRef, (void *)usageNumRef };
+ retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ }
+
+ if (pageNumRef) {
+ CFRelease(pageNumRef);
+ }
+ if (usageNumRef) {
+ CFRelease(usageNumRef);
+ }
+
+ if (!retval) {
+ *okay = 0;
+ }
+
+ return retval;
+}
+
+void MacOsJoystickInput::config_hid_manager(CFArrayRef p_matching_array) const
+{
+ CFRunLoopRef runloop = CFRunLoopGetCurrent();
+ IOReturn ret = IOHIDManagerOpen(m_hidManager, kIOHIDOptionsTypeNone);
+ Q_ASSERT(ret == kIOReturnSuccess);
+
+ IOHIDManagerSetDeviceMatchingMultiple(m_hidManager, p_matching_array);
+ IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, joypad_added_callback, nullptr);
+ IOHIDManagerRegisterDeviceRemovalCallback(m_hidManager, joypad_removed_callback, nullptr);
+ IOHIDManagerScheduleWithRunLoop(m_hidManager, runloop, JOYPAD_LOOP_RUN_MODE);
+
+ while (CFRunLoopRunInMode(JOYPAD_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
+ // No-op. Callback fires once per existing device.
+ }
+}
+
+MacOsJoystickInput::MacOsJoystickInput()
+{
+ self = this;
+
+ int okay = 1;
+ const void *vals[] = {
+ (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
+ (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
+ (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
+ };
+ const size_t n_elements = sizeof(vals) / sizeof(vals[0]);
+ CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, n_elements, &kCFTypeArrayCallBacks) : nullptr;
+
+ for (size_t i = 0; i < n_elements; i++)
+ if (vals[i])
+ CFRelease((CFTypeRef)vals[i]);
+
+ if (array) {
+ m_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+ if (m_hidManager)
+ config_hid_manager(array);
+
+ CFRelease(array);
+ }
+
+ startTimer(1);
+}
+
+MacOsJoystickInput::~MacOsJoystickInput()
+{
+ for (auto &joy : m_deviceList)
+ joy.free();
+ m_deviceList.clear();
+
+ IOHIDManagerUnscheduleFromRunLoop(m_hidManager, CFRunLoopGetCurrent(), JOYPAD_LOOP_RUN_MODE);
+ IOHIDManagerClose(m_hidManager, kIOHIDOptionsTypeNone);
+ CFRelease(m_hidManager);
+ m_hidManager = nullptr;
+}
+
+void MacOsJoystickInput::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event);
+ processJoypads();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/macos/macosjoystickinputplugin.cpp b/src/plugins/joystickinputs/macos/macosjoystickinputplugin.cpp
new file mode 100644
index 0000000..4128dc2
--- /dev/null
+++ b/src/plugins/joystickinputs/macos/macosjoystickinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "macosjoystickinputplugin.h"
+#include "macosjoystickinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QJoystickInput *MacOsJoystickInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("macos"))
+ return new MacOsJoystickInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/macos/macosjoystickinputplugin.h b/src/plugins/joystickinputs/macos/macosjoystickinputplugin.h
new file mode 100644
index 0000000..8c711ba
--- /dev/null
+++ b/src/plugins/joystickinputs/macos/macosjoystickinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef MACOSJOYSTICKINPUTPLUGIN_H
+#define MACOSJOYSTICKINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qjoystickinputplugin_p.h>
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class MacOsJoystickInputPlugin : public QJoystickInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "macos.json")
+
+public:
+ QJoystickInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // MACOSJOYSTICKINPUTPLUGIN_H
diff --git a/src/plugins/joystickinputs/windows/CMakeLists.txt b/src/plugins/joystickinputs/windows/CMakeLists.txt
new file mode 100644
index 0000000..30ab5b1
--- /dev/null
+++ b/src/plugins/joystickinputs/windows/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(WindowsJoystickInputPlugin
+ OUTPUT_NAME windowsjoystickinput
+ PLUGIN_TYPE joystickinputs
+ SOURCES
+ windowsjoystickinput.cpp windowsjoystickinput.h
+ windowsjoystickinputplugin.cpp windowsjoystickinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+ dinput8
+)
diff --git a/src/plugins/joystickinputs/windows/windows.json b/src/plugins/joystickinputs/windows/windows.json
new file mode 100644
index 0000000..05032c1
--- /dev/null
+++ b/src/plugins/joystickinputs/windows/windows.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "windows" ]
+}
diff --git a/src/plugins/joystickinputs/windows/windowsjoystickinput.cpp b/src/plugins/joystickinputs/windows/windowsjoystickinput.cpp
new file mode 100644
index 0000000..00a12b1
--- /dev/null
+++ b/src/plugins/joystickinputs/windows/windowsjoystickinput.cpp
@@ -0,0 +1,571 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/windows/joypad_windows.cpp" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "windowsjoystickinput.h"
+
+#include <QtUniversalInput/private/quniversalinput_p.h>
+
+#include <QtGui/QGuiApplication>
+#include <QtGui/QWindow>
+
+#include <QtCore/QDateTime>
+
+#include <system_error>
+
+DWORD WINAPI _xinput_get_state(DWORD dwUserIndex, XINPUT_STATE *pState)
+{
+ return ERROR_DEVICE_NOT_CONNECTED;
+}
+
+DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration)
+{
+ return ERROR_DEVICE_NOT_CONNECTED;
+}
+
+QT_BEGIN_NAMESPACE
+
+// Function to convert HRESULT to QString
+static QString convertHRESULTToQString(HRESULT hr)
+{
+ char *msgBuf = nullptr;
+ FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msgBuf, 0, nullptr);
+ QString msg = QString::fromLocal8Bit(msgBuf);
+ LocalFree(msgBuf);
+ return msg;
+}
+
+WindowsJoystickInput::WindowsJoystickInput()
+{
+ auto qtApp = QGuiApplication::instance();
+ if (qtApp && qApp->focusWindow())
+ hWnd = (HWND)qApp->focusWindow()->winId();
+ // ### Maybe we should delay the initialization until we know there is an available window?
+
+ load_xinput();
+
+ for (int i = 0; i < JOYPADS_MAX; i++)
+ attached_joypads[i] = false;
+
+ HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
+ if (result == DI_OK) {
+ probeJoypads();
+ } else {
+ qWarning() << "Couldn't initialize DirectInput. Error: " << convertHRESULTToQString(result);
+ if (result == DIERR_OUTOFMEMORY) {
+ qWarning("The Windows DirectInput subsystem could not allocate sufficient memory.");
+ qWarning("Rebooting your PC may solve this issue.");
+ }
+ // Ensure dinput is still a nullptr.
+ dinput = nullptr;
+ }
+
+ // ### Replace with Thread later
+ startTimer(1);
+}
+
+WindowsJoystickInput::~WindowsJoystickInput()
+{
+ close_joypad();
+ if (dinput)
+ dinput->Release();
+ unload_xinput();
+}
+
+void WindowsJoystickInput::probeJoypads()
+{
+ if (dinput == nullptr) {
+ qWarning("DirectInput not initialized. Rebooting your PC may solve this issue.");
+ return;
+ }
+ DWORD dwResult;
+ auto input = QUniversalInput::instance();
+ for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) {
+ ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE));
+
+ dwResult = xinput_get_state(i, &x_joypads[i].state);
+ if (dwResult == ERROR_SUCCESS) {
+ int id = input->getUnusedJoyId();
+ if (id != -1 && !x_joypads[i].attached) {
+ x_joypads[i].attached = true;
+ x_joypads[i].id = id;
+ x_joypads[i].ff_timestamp = 0;
+ x_joypads[i].ff_end_timestamp = 0;
+ x_joypads[i].vibrating = false;
+ attached_joypads[id] = true;
+ input->updateJoyConnection(id, true, "XInput Gamepad", "__XINPUT_DEVICE__");
+ }
+ } else if (x_joypads[i].attached) {
+ x_joypads[i].attached = false;
+ attached_joypads[x_joypads[i].id] = false;
+ input->updateJoyConnection(x_joypads[i].id, false, "");
+ }
+ }
+
+ for (int i = 0; i < joypad_count; i++) {
+ d_joypads[i].confirmed = false;
+ }
+
+ dinput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, this, DIEDFL_ATTACHEDONLY);
+
+ for (int i = 0; i < joypad_count; i++) {
+ if (!d_joypads[i].confirmed) {
+ close_joypad(i);
+ }
+ }
+}
+
+void WindowsJoystickInput::processJoypads()
+{
+ HRESULT hr;
+ auto input = QUniversalInput::instance();
+ for (int i = 0; i < XUSER_MAX_COUNT; i++) {
+ xinput_gamepad &joy = x_joypads[i];
+ if (!joy.attached) {
+ continue;
+ }
+ ZeroMemory(&joy.state, sizeof(XINPUT_STATE));
+
+ xinput_get_state(i, &joy.state);
+ if (joy.state.dwPacketNumber != joy.last_packet) {
+ int button_mask = XINPUT_GAMEPAD_DPAD_UP;
+ for (int j = 0; j <= 16; j++) {
+ input->joyButton(joy.id, (JoyButton)j, joy.state.Gamepad.wButtons & button_mask);
+ button_mask = button_mask * 2;
+ }
+
+ input->joyAxis(joy.id, JoyAxis::LeftX, axis_correct(joy.state.Gamepad.sThumbLX, true));
+ input->joyAxis(joy.id, JoyAxis::LeftY, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true));
+ input->joyAxis(joy.id, JoyAxis::RightX, axis_correct(joy.state.Gamepad.sThumbRX, true));
+ input->joyAxis(joy.id, JoyAxis::RightY, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true));
+ input->joyAxis(joy.id, JoyAxis::TriggerLeft, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true));
+ input->joyAxis(joy.id, JoyAxis::TriggerRight, axis_correct(joy.state.Gamepad.bRightTrigger, true, true));
+ joy.last_packet = joy.state.dwPacketNumber;
+ }
+ uint64_t timestamp = input->getJoyVibrationTimestamp(joy.id);
+ if (timestamp > joy.ff_timestamp) {
+ QVector2D strength = input->getJoyVibrationStrength(joy.id);
+ float duration = input->getJoyVibrationDuration(joy.id);
+ if (strength.x() == 0 && strength.y() == 0) {
+ joypad_vibration_stop_xinput(i, timestamp);
+ } else {
+ joypad_vibration_start_xinput(i, strength.x(), strength.y(), duration, timestamp);
+ }
+ } else if (joy.vibrating && joy.ff_end_timestamp != 0) {
+ uint64_t currentTime = QDateTime::currentMSecsSinceEpoch();
+ if (currentTime >= joy.ff_end_timestamp)
+ joypad_vibration_stop_xinput(i, currentTime);
+ }
+ }
+
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ dinput_gamepad *joy = &d_joypads[i];
+
+ if (!joy->attached) {
+ continue;
+ }
+
+ DIJOYSTATE2 js;
+ hr = joy->di_joy->Poll();
+ if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) {
+ IDirectInputDevice8_Acquire(joy->di_joy);
+ joy->di_joy->Poll();
+ }
+
+ hr = joy->di_joy->GetDeviceState(sizeof(DIJOYSTATE2), &js);
+ if (FAILED(hr)) {
+ continue;
+ }
+
+ post_hat(joy->id, js.rgdwPOV[0]);
+
+ for (int j = 0; j < 128; j++) {
+ if (js.rgbButtons[j] & 0x80) {
+ if (!joy->last_buttons[j]) {
+ input->joyButton(joy->id, (JoyButton)j, true);
+ joy->last_buttons[j] = true;
+ }
+ } else {
+ if (joy->last_buttons[j]) {
+ input->joyButton(joy->id, (JoyButton)j, false);
+ joy->last_buttons[j] = false;
+ }
+ }
+ }
+
+ // on mingw, these constants are not constants
+ int count = 8;
+ const LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
+ int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] };
+
+ for (int j = 0; j < joy->joy_axis.size(); j++) {
+ for (int k = 0; k < count; k++) {
+ if (joy->joy_axis[j] == axes[k]) {
+ input->joyAxis(joy->id, (JoyAxis)j, axis_correct(values[k]));
+ break;
+ }
+ }
+ }
+ }
+}
+
+void WindowsJoystickInput::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event);
+ processJoypads();
+}
+
+bool WindowsJoystickInput::have_device(const GUID &p_guid)
+{
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ if (d_joypads[i].guid == p_guid) {
+ d_joypads[i].confirmed = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WindowsJoystickInput::is_xinput_device(const GUID *p_guid)
+{
+ static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x28DE, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
+ static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
+ static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
+
+ if (::memcmp(p_guid, &IID_ValveStreamingGamepad, sizeof(*p_guid)) == 0 ||
+ ::memcmp(p_guid, &IID_X360WiredGamepad, sizeof(*p_guid)) == 0 ||
+ ::memcmp(p_guid, &IID_X360WirelessGamepad, sizeof(*p_guid)) == 0)
+ return true;
+
+ PRAWINPUTDEVICELIST dev_list = nullptr;
+ unsigned int dev_list_count = 0;
+
+ if (GetRawInputDeviceList(nullptr, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1)
+ return false;
+
+ dev_list = (PRAWINPUTDEVICELIST)::malloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count);
+ if (dev_list == nullptr) {
+ qWarning("Out of memory.");
+ return false;
+ }
+
+ if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
+ ::free(dev_list);
+ return false;
+ }
+ for (unsigned int i = 0; i < dev_list_count; i++) {
+ RID_DEVICE_INFO rdi;
+ char dev_name[128];
+ UINT rdiSize = sizeof(rdi);
+ UINT nameSize = sizeof(dev_name);
+
+ rdi.cbSize = rdiSize;
+ if ((dev_list[i].dwType == RIM_TYPEHID) &&
+ (GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT)-1) &&
+ (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)p_guid->Data1) &&
+ (GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICENAME, &dev_name, &nameSize) != (UINT)-1) &&
+ (strstr(dev_name, "IG_") != nullptr)) {
+ ::free(dev_list);
+ return true;
+ }
+ }
+ ::free(dev_list);
+ return false;
+}
+
+static inline uint16_t BSWAP16(uint16_t x)
+{
+ return (x >> 8) | (x << 8);
+}
+
+bool WindowsJoystickInput::setup_dinput_joypad(const DIDEVICEINSTANCE *instance)
+{
+ if (dinput == nullptr) {
+ qWarning("DirectInput not initialized. Rebooting your PC may solve this issue.");
+ return false;
+ }
+
+ HRESULT hr;
+ auto input = QUniversalInput::instance();
+ int num = input->getUnusedJoyId();
+
+ if (have_device(instance->guidInstance) || num == -1)
+ return false;
+
+ d_joypads[num] = dinput_gamepad();
+ dinput_gamepad *joy = &d_joypads[num];
+
+ const DWORD devtype = (instance->dwDevType & 0xFF);
+
+ if ((devtype != DI8DEVTYPE_JOYSTICK) && (devtype != DI8DEVTYPE_GAMEPAD) && (devtype != DI8DEVTYPE_1STPERSON) && (devtype != DI8DEVTYPE_DRIVING))
+ return false;
+
+ hr = dinput->CreateDevice(instance->guidInstance, &joy->di_joy, nullptr);
+
+ if (FAILED(hr))
+ return false;
+
+ const GUID &guid = instance->guidProduct;
+ char uid[128];
+
+ if (memcmp(&guid.Data4[2], "PIDVID", 6) != 0) {
+ qWarning("DirectInput device not recognized.");
+ return false;
+ }
+
+ WORD type = BSWAP16(0x03);
+ WORD vendor = BSWAP16(LOWORD(guid.Data1));
+ WORD product = BSWAP16(HIWORD(guid.Data1));
+ WORD version = 0;
+ sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0);
+
+ id_to_change = num;
+ slider_count = 0;
+
+ joy->di_joy->SetDataFormat(&c_dfDIJoystick2);
+ joy->di_joy->SetCooperativeLevel(hWnd, DISCL_FOREGROUND);
+ joy->di_joy->EnumObjects(objectsCallback, this, 0);
+ std::sort(joy->joy_axis.begin(), joy->joy_axis.end());
+
+ joy->guid = instance->guidInstance;
+ input->updateJoyConnection(num, true, QString::fromWCharArray(instance->tszProductName), QString::fromLocal8Bit(uid));
+ joy->attached = true;
+ joy->id = num;
+ attached_joypads[num] = true;
+ joy->confirmed = true;
+ joypad_count++;
+ return true;
+}
+
+void WindowsJoystickInput::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id)
+{
+ if (ob->dwType & DIDFT_AXIS) {
+ HRESULT res;
+ DIPROPRANGE prop_range;
+ DIPROPDWORD dilong;
+ LONG ofs;
+ if (ob->guidType == GUID_XAxis) {
+ ofs = DIJOFS_X;
+ } else if (ob->guidType == GUID_YAxis) {
+ ofs = DIJOFS_Y;
+ } else if (ob->guidType == GUID_ZAxis) {
+ ofs = DIJOFS_Z;
+ } else if (ob->guidType == GUID_RxAxis) {
+ ofs = DIJOFS_RX;
+ } else if (ob->guidType == GUID_RyAxis) {
+ ofs = DIJOFS_RY;
+ } else if (ob->guidType == GUID_RzAxis) {
+ ofs = DIJOFS_RZ;
+ } else if (ob->guidType == GUID_Slider) {
+ if (slider_count < 2) {
+ ofs = DIJOFS_SLIDER(slider_count);
+ slider_count++;
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ prop_range.diph.dwSize = sizeof(DIPROPRANGE);
+ prop_range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+ prop_range.diph.dwObj = ob->dwType;
+ prop_range.diph.dwHow = DIPH_BYID;
+ prop_range.lMin = -MAX_JOY_AXIS;
+ prop_range.lMax = +MAX_JOY_AXIS;
+
+ dinput_gamepad &joy = d_joypads[p_joy_id];
+
+ res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_RANGE, &prop_range.diph);
+ if (FAILED(res)) {
+ return;
+ }
+
+ dilong.diph.dwSize = sizeof(dilong);
+ dilong.diph.dwHeaderSize = sizeof(dilong.diph);
+ dilong.diph.dwObj = ob->dwType;
+ dilong.diph.dwHow = DIPH_BYID;
+ dilong.dwData = 0;
+
+ res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_DEADZONE, &dilong.diph);
+ if (FAILED(res)) {
+ return;
+ }
+
+ joy.joy_axis.push_back(ofs);
+ }
+}
+
+BOOL CALLBACK WindowsJoystickInput::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context)
+{
+ WindowsJoystickInput *self = static_cast<WindowsJoystickInput *>(p_context);
+ if (self->is_xinput_device(&p_instance->guidProduct)) {
+ return DIENUM_CONTINUE;
+ }
+ self->setup_dinput_joypad(p_instance);
+ return DIENUM_CONTINUE;
+}
+
+BOOL CALLBACK WindowsJoystickInput::objectsCallback(const DIDEVICEOBJECTINSTANCE *p_instance, void *p_context)
+{
+ WindowsJoystickInput *self = static_cast<WindowsJoystickInput *>(p_context);
+ self->setup_joypad_object(p_instance, self->id_to_change);
+
+ return DIENUM_CONTINUE;
+}
+
+void WindowsJoystickInput::close_joypad(int id)
+{
+ if (id == -1) {
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ close_joypad(i);
+ }
+ return;
+ }
+
+ if (!d_joypads[id].attached) {
+ return;
+ }
+
+ d_joypads[id].di_joy->Unacquire();
+ d_joypads[id].di_joy->Release();
+ d_joypads[id].attached = false;
+ attached_joypads[d_joypads[id].id] = false;
+ d_joypads[id].guid.Data1 = d_joypads[id].guid.Data2 = d_joypads[id].guid.Data3 = 0;
+ QUniversalInput::instance()->updateJoyConnection(d_joypads[id].id, false, "");
+ joypad_count--;
+}
+
+void WindowsJoystickInput::post_hat(int p_device, DWORD p_dpad)
+{
+ HatMask dpad_val = (HatMask)0;
+
+ // Should be -1 when centered, but according to docs:
+ // "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows:
+ // BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
+ // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
+ if (LOWORD(p_dpad) == 0xFFFF)
+ dpad_val = (HatMask)HatMask::Center;
+
+ if (p_dpad == 0) {
+ dpad_val = (HatMask)HatMask::Up;
+
+ } else if (p_dpad == 4500) {
+ dpad_val = (HatMask)(HatMask::Up | HatMask::Right);
+
+ } else if (p_dpad == 9000) {
+ dpad_val = (HatMask)HatMask::Right;
+
+ } else if (p_dpad == 13500) {
+ dpad_val = (HatMask)(HatMask::Right | HatMask::Down);
+
+ } else if (p_dpad == 18000) {
+ dpad_val = (HatMask)HatMask::Down;
+
+ } else if (p_dpad == 22500) {
+ dpad_val = (HatMask)(HatMask::Down | HatMask::Left);
+
+ } else if (p_dpad == 27000) {
+ dpad_val = (HatMask)HatMask::Left;
+
+ } else if (p_dpad == 31500) {
+ dpad_val = (HatMask)(HatMask::Left | HatMask::Up);
+ }
+ QUniversalInput::instance()->joyHat(p_device, dpad_val);
+}
+
+float WindowsJoystickInput::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const
+{
+ if (qFabs(p_val) < MIN_JOY_AXIS)
+ return p_trigger ? -1.0f : 0.0f;
+
+ if (!p_xinput)
+ return (float)p_val / MAX_JOY_AXIS;
+
+ // Convert to a value between -1.0f and 1.0f.
+ if (p_trigger)
+ return 2.0f * p_val / MAX_TRIGGER - 1.0f;
+
+ float value;
+ if (p_val < 0)
+ value = (float)p_val / MAX_JOY_AXIS;
+ else
+ value = (float)p_val / (MAX_JOY_AXIS - 1);
+
+ if (p_negate)
+ value = -value;
+
+ return value;
+}
+
+void WindowsJoystickInput::joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp)
+{
+ xinput_gamepad &joy = x_joypads[p_device];
+ if (joy.attached) {
+ XINPUT_VIBRATION effect;
+ effect.wLeftMotorSpeed = (65535 * p_strong_magnitude);
+ effect.wRightMotorSpeed = (65535 * p_weak_magnitude);
+ if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
+ joy.ff_timestamp = p_timestamp;
+ joy.ff_end_timestamp = p_duration == 0 ? 0 : p_timestamp + (uint64_t)(p_duration * 1000);
+ joy.vibrating = true;
+ }
+ }
+}
+
+void WindowsJoystickInput::joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp)
+{
+ xinput_gamepad &joy = x_joypads[p_device];
+ if (joy.attached) {
+ XINPUT_VIBRATION effect;
+ effect.wLeftMotorSpeed = 0;
+ effect.wRightMotorSpeed = 0;
+ if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
+ joy.ff_timestamp = p_timestamp;
+ joy.vibrating = false;
+ }
+ }
+}
+
+void WindowsJoystickInput::load_xinput()
+{
+ xinput_get_state = &_xinput_get_state;
+ xinput_set_state = &_xinput_set_state;
+ xinput_dll.setFileName("xinput1_4.dll");
+ if (!xinput_dll.load()) {
+ xinput_dll.setFileName("xinput1_3.dll");
+ if (!xinput_dll.load()) {
+ xinput_dll.setFileName("xinput9_1_0.dll");
+ xinput_dll.load();
+ }
+ }
+
+ if (!xinput_dll.isLoaded()) {
+ qWarning("Could not find XInput, using DirectInput only");
+ return;
+ }
+
+ XInputGetState_t func = (XInputGetState_t)xinput_dll.resolve("XInputGetState");
+ XInputSetState_t set_func = (XInputSetState_t)xinput_dll.resolve("XInputSetState");
+
+ if (!func || !set_func) {
+ unload_xinput();
+ return;
+ }
+ xinput_get_state = func;
+ xinput_set_state = set_func;
+}
+
+void WindowsJoystickInput::unload_xinput()
+{
+ if (xinput_dll.isLoaded())
+ xinput_dll.unload();
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/windows/windowsjoystickinput.h b/src/plugins/joystickinputs/windows/windowsjoystickinput.h
new file mode 100644
index 0000000..eaab459
--- /dev/null
+++ b/src/plugins/joystickinputs/windows/windowsjoystickinput.h
@@ -0,0 +1,123 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/windows/joypad_windows.h" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#ifndef WINDOWSJOYSTICKINPUT_H
+#define WINDOWSJOYSTICKINPUT_H
+
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+#include <QtCore/QLibrary>
+
+
+#include <windows.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+#include <xinput.h>
+
+QT_BEGIN_NAMESPACE
+
+class WindowsJoystickInput : public QJoystickInput
+{
+ Q_OBJECT
+public:
+ WindowsJoystickInput();
+ ~WindowsJoystickInput();
+
+ void probeJoypads();
+ void processJoypads();
+
+protected:
+ void timerEvent(QTimerEvent *event) override;
+
+private:
+ enum {
+ JOYPADS_MAX = 16,
+ JOY_AXIS_COUNT = 6,
+ MIN_JOY_AXIS = 10,
+ MAX_JOY_AXIS = 32768,
+ MAX_JOY_BUTTONS = 128,
+ KEY_EVENT_BUFFER_SIZE = 512,
+ MAX_TRIGGER = 255
+ };
+
+ struct dinput_gamepad {
+ int id;
+ bool attached;
+ bool confirmed;
+ bool last_buttons[MAX_JOY_BUTTONS];
+ DWORD last_pad;
+
+ LPDIRECTINPUTDEVICE8 di_joy;
+ QList<LONG> joy_axis;
+ GUID guid;
+
+ dinput_gamepad() {
+ id = -1;
+ last_pad = -1;
+ attached = false;
+ confirmed = false;
+ di_joy = nullptr;
+ guid = {};
+
+ for (int i = 0; i < MAX_JOY_BUTTONS; i++) {
+ last_buttons[i] = false;
+ }
+ }
+ };
+
+ struct xinput_gamepad {
+ int id = 0;
+ bool attached = false;
+ bool vibrating = false;
+ DWORD last_packet = 0;
+ XINPUT_STATE state;
+ uint64_t ff_timestamp = 0;
+ uint64_t ff_end_timestamp = 0;
+ };
+
+ typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState);
+ typedef DWORD(WINAPI *XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
+
+ HWND hWnd = nullptr;
+ LPDIRECTINPUT8 dinput = nullptr;
+
+ int id_to_change;
+ int slider_count;
+ int joypad_count;
+ bool attached_joypads[JOYPADS_MAX];
+ dinput_gamepad d_joypads[JOYPADS_MAX];
+ xinput_gamepad x_joypads[XUSER_MAX_COUNT];
+
+ static BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context);
+ static BOOL CALLBACK objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context);
+
+ void setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id);
+ void close_joypad(int id = -1);
+ void load_xinput();
+ void unload_xinput();
+
+ void post_hat(int p_device, DWORD p_dpad);
+
+ bool have_device(const GUID &p_guid);
+ bool is_xinput_device(const GUID *p_guid);
+ bool setup_dinput_joypad(const DIDEVICEINSTANCE *instance);
+ void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
+ void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
+
+ float axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
+
+ QLibrary xinput_dll;
+ XInputGetState_t xinput_get_state = nullptr;
+ XInputSetState_t xinput_set_state = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // WINDOWSJOYSTICKINPUT_H
diff --git a/src/plugins/joystickinputs/windows/windowsjoystickinputplugin.cpp b/src/plugins/joystickinputs/windows/windowsjoystickinputplugin.cpp
new file mode 100644
index 0000000..da98281
--- /dev/null
+++ b/src/plugins/joystickinputs/windows/windowsjoystickinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "windowsjoystickinputplugin.h"
+#include "windowsjoystickinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QJoystickInput *WindowsJoystickInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("windows"))
+ return new WindowsJoystickInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/joystickinputs/windows/windowsjoystickinputplugin.h b/src/plugins/joystickinputs/windows/windowsjoystickinputplugin.h
new file mode 100644
index 0000000..a7fb726
--- /dev/null
+++ b/src/plugins/joystickinputs/windows/windowsjoystickinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef WINDOWSJOYSTICKINPUTPLUGIN_H
+#define WINDOWSJOYSTICKINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qjoystickinputplugin_p.h>
+#include <QtUniversalInput/private/qjoystickinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class WindowsJoystickInputPlugin : public QJoystickInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QJoystickInputFactoryInterface_iid FILE "windows.json")
+
+public:
+ QJoystickInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // WINDOWSJOYSTICKINPUTPLUGIN_H
diff --git a/src/plugins/mouseinputs/CMakeLists.txt b/src/plugins/mouseinputs/CMakeLists.txt
new file mode 100644
index 0000000..156e04e
--- /dev/null
+++ b/src/plugins/mouseinputs/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# for each platform, add a subdirectory with the platform name
+#if(ANDROID)
+# add_subdirectory(android)
+#endif()
+if(MACOS)
+ add_subdirectory(macos)
+endif()
+#if(IOS)
+# add_subdirectory(ios)
+#endif()
+if(WIN32)
+ add_subdirectory(windows)
+endif()
+if(LINUX)
+ add_subdirectory(linux)
+endif()
+
+#add_subdirectory(macos)
+#add_subdirectory(ios)
+#add_subdirectory(windows)
diff --git a/src/plugins/mouseinputs/linux/CMakeLists.txt b/src/plugins/mouseinputs/linux/CMakeLists.txt
new file mode 100644
index 0000000..7d1da23
--- /dev/null
+++ b/src/plugins/mouseinputs/linux/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(LinuxMouseInputPlugin
+ OUTPUT_NAME linuxmouseinput
+ PLUGIN_TYPE mouseinputs
+ SOURCES
+ linuxmouseinput.cpp linuxmouseinput.h
+ linuxmouseinputplugin.cpp linuxmouseinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+)
diff --git a/src/plugins/mouseinputs/linux/linux.json b/src/plugins/mouseinputs/linux/linux.json
new file mode 100644
index 0000000..8165d39
--- /dev/null
+++ b/src/plugins/mouseinputs/linux/linux.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "linux" ]
+}
diff --git a/src/plugins/mouseinputs/linux/linuxmouseinput.cpp b/src/plugins/mouseinputs/linux/linuxmouseinput.cpp
new file mode 100644
index 0000000..87cad43
--- /dev/null
+++ b/src/plugins/mouseinputs/linux/linuxmouseinput.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.cpp" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "linuxmouseinput.h"
+#include <QtUniversalInput/QUniversalInput>
+#include <QtEndian>
+
+#include <QCursor>
+#include <QGuiApplication>
+#include <QWindow>
+#include <QDateTime>
+
+#include <QCursor>
+
+QT_BEGIN_NAMESPACE
+
+static LinuxMouseInput *self = nullptr;
+
+LinuxMouseInput::LinuxMouseInput()
+{
+ self = this;
+
+ m_timer = new QTimer(this);
+ m_timer->setInterval(1);
+ connect(m_timer, &QTimer::timeout, this, &LinuxMouseInput::onUpdate);
+ m_timer->start();
+}
+
+LinuxMouseInput::~LinuxMouseInput()
+{
+ self = nullptr;
+ m_timer->stop();
+ delete m_timer;
+}
+
+QVector2D LinuxMouseInput::getMouseDelta()
+{
+ static QPoint lastPos = QCursor::pos();
+ QPoint newPos = QCursor::pos();
+ QVector2D delta = QVector2D(newPos.toPointF() - lastPos.toPointF());
+ lastPos = newPos;
+ return delta;
+}
+
+void LinuxMouseInput::setCursorCenterOfWindow()
+{
+ QWindow* window = QGuiApplication::focusWindow();
+ if (!window)
+ return;
+ QPoint center = window->geometry().center();
+ QCursor::setPos(center);
+}
+
+void LinuxMouseInput::onUpdate()
+{
+ if (!self)
+ return;
+
+ auto input = QUniversalInput::instance();
+
+ QVector2D delta = getMouseDelta();
+ if (delta.x() == 0 && delta.y() == 0)
+ return;
+
+ input->mouseMove(delta);
+
+ if (input->isMouseDisabled()) {
+ setCursorCenterOfWindow();
+ // set cursor to blank
+ QGuiApplication::setOverrideCursor(Qt::BlankCursor);
+ m_wasDisabled = true;
+ } else if (m_wasDisabled) {
+ QGuiApplication::setOverrideCursor(Qt::ArrowCursor);
+ m_wasDisabled = false;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/mouseinputs/linux/linuxmouseinput.h b/src/plugins/mouseinputs/linux/linuxmouseinput.h
new file mode 100644
index 0000000..bc67ce4
--- /dev/null
+++ b/src/plugins/mouseinputs/linux/linuxmouseinput.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#ifndef LINUXMOUSEINPUT_H
+#define LINUXMOUSEINPUT_H
+
+#include <QtUniversalInput/private/qmouseinput_p.h>
+
+#include <QTimer>
+#include <QVector2D>
+
+QT_BEGIN_NAMESPACE
+
+class LinuxMouseInput : public QMouseInput
+{
+ Q_OBJECT
+public:
+ LinuxMouseInput();
+ ~LinuxMouseInput();
+
+ QVector2D getMouseDelta();
+ void setCursorCenterOfWindow();
+
+
+public Q_SLOTS:
+ void onUpdate();
+
+private:
+ QTimer* m_timer = nullptr;
+ bool m_wasDisabled = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // LINUXMOUSEINPUT_H
diff --git a/src/plugins/mouseinputs/linux/linuxmouseinputplugin.cpp b/src/plugins/mouseinputs/linux/linuxmouseinputplugin.cpp
new file mode 100644
index 0000000..31a1bf1
--- /dev/null
+++ b/src/plugins/mouseinputs/linux/linuxmouseinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "linuxmouseinputplugin.h"
+#include "linuxmouseinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QMouseInput *LinuxMouseInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("linux"))
+ return new LinuxMouseInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/mouseinputs/linux/linuxmouseinputplugin.h b/src/plugins/mouseinputs/linux/linuxmouseinputplugin.h
new file mode 100644
index 0000000..a577f49
--- /dev/null
+++ b/src/plugins/mouseinputs/linux/linuxmouseinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef LINUXMOUSEINPUTPLUGIN_H
+#define LINUXMOUSEINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qmouseinputplugin_p.h>
+#include <QtUniversalInput/private/qmouseinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class LinuxMouseInputPlugin : public QMouseInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QMouseInputFactoryInterface_iid FILE "linux.json")
+
+public:
+ QMouseInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // LINUXMOUSEINPUTPLUGIN_H
diff --git a/src/plugins/mouseinputs/macos/CMakeLists.txt b/src/plugins/mouseinputs/macos/CMakeLists.txt
new file mode 100644
index 0000000..5d1005e
--- /dev/null
+++ b/src/plugins/mouseinputs/macos/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_find_apple_system_framework(FWAppKit AppKit)
+
+qt_internal_add_plugin(MacOSMouseInputPlugin
+ OUTPUT_NAME macosmouseinput
+ PLUGIN_TYPE mouseinputs
+ SOURCES
+ macosmouseinput.mm macosmouseinput.h
+ macosmouseinputplugin.cpp macosmouseinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+ ${FWAppKit}
+)
diff --git a/src/plugins/mouseinputs/macos/macos.json b/src/plugins/mouseinputs/macos/macos.json
new file mode 100644
index 0000000..a3f3787
--- /dev/null
+++ b/src/plugins/mouseinputs/macos/macos.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "macos" ]
+}
diff --git a/src/plugins/mouseinputs/macos/macosmouseinput.h b/src/plugins/mouseinputs/macos/macosmouseinput.h
new file mode 100644
index 0000000..aefdc2e
--- /dev/null
+++ b/src/plugins/mouseinputs/macos/macosmouseinput.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#ifndef MACOSMOUSEINPUT_H
+#define MACOSMOUSEINPUT_H
+
+#include <QtUniversalInput/private/qmouseinput_p.h>
+
+#include <QTimer>
+#include <QVector2D>
+
+QT_BEGIN_NAMESPACE
+
+class MacOsMouseInput : public QMouseInput
+{
+ Q_OBJECT
+public:
+ MacOsMouseInput();
+ ~MacOsMouseInput();
+
+ QVector2D getMouseDelta();
+ void setCursorCenterOfWindow();
+
+
+public Q_SLOTS:
+ void onUpdate();
+
+private:
+ QTimer* m_timer = nullptr;
+ bool m_wasDisabled = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // MACOSMOUSEINPUT_H
diff --git a/src/plugins/mouseinputs/macos/macosmouseinput.mm b/src/plugins/mouseinputs/macos/macosmouseinput.mm
new file mode 100644
index 0000000..abd2a3d
--- /dev/null
+++ b/src/plugins/mouseinputs/macos/macosmouseinput.mm
@@ -0,0 +1,82 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.cpp" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "macosmouseinput.h"
+#include <QtUniversalInput/QUniversalInput>
+#include <QtEndian>
+
+#include <AppKit/AppKit.h>
+#include <CoreGraphics/CoreGraphics.h>
+
+#include <QCursor>
+#include <QGuiApplication>
+
+QT_BEGIN_NAMESPACE
+
+static MacOsMouseInput *self = nullptr;
+
+MacOsMouseInput::MacOsMouseInput()
+{
+ self = this;
+
+ m_timer = new QTimer(this);
+ m_timer->setInterval(1);
+ connect(m_timer, &QTimer::timeout, this, &MacOsMouseInput::onUpdate);
+ m_timer->start();
+}
+
+MacOsMouseInput::~MacOsMouseInput()
+{
+ self = nullptr;
+ m_timer->stop();
+ delete m_timer;
+}
+
+QVector2D MacOsMouseInput::getMouseDelta()
+{
+ // kind of a hack, but it works for now
+ static NSPoint previousLocation;
+ NSPoint currentLocation = [NSEvent mouseLocation];
+ CGPoint delta = CGPointMake(currentLocation.x - previousLocation.x, currentLocation.y - previousLocation.y);
+ previousLocation = currentLocation;
+ return QVector2D(float(delta.x), float(delta.y));
+}
+
+void MacOsMouseInput::setCursorCenterOfWindow()
+{
+ NSRect windowRect = [[NSApp mainWindow] frame];
+ NSPoint windowCenter = NSMakePoint(NSMidX(windowRect), NSMidY(windowRect));
+ CGWarpMouseCursorPosition(windowCenter);
+}
+
+void MacOsMouseInput::onUpdate()
+{
+ if (!self)
+ return;
+
+ auto input = QUniversalInput::instance();
+
+ QVector2D delta = getMouseDelta();
+ if (delta.x() == 0 && delta.y() == 0)
+ return;
+
+ input->mouseMove(delta);
+
+ if (input->isMouseDisabled()) {
+ setCursorCenterOfWindow();
+ // set cursor to blank
+ QGuiApplication::setOverrideCursor(Qt::BlankCursor);
+ m_wasDisabled = true;
+ } else if (m_wasDisabled) {
+ QGuiApplication::setOverrideCursor(Qt::ArrowCursor);
+ m_wasDisabled = false;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/mouseinputs/macos/macosmouseinputplugin.cpp b/src/plugins/mouseinputs/macos/macosmouseinputplugin.cpp
new file mode 100644
index 0000000..88411ac
--- /dev/null
+++ b/src/plugins/mouseinputs/macos/macosmouseinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "macosmouseinputplugin.h"
+#include "macosmouseinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QMouseInput *MacOsMouseInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("macos"))
+ return new MacOsMouseInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/mouseinputs/macos/macosmouseinputplugin.h b/src/plugins/mouseinputs/macos/macosmouseinputplugin.h
new file mode 100644
index 0000000..6245974
--- /dev/null
+++ b/src/plugins/mouseinputs/macos/macosmouseinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef MACOSMOUSEINPUTPLUGIN_H
+#define MACOSMOUSEINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qmouseinputplugin_p.h>
+#include <QtUniversalInput/private/qmouseinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class MacOsMouseInputPlugin : public QMouseInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QMouseInputFactoryInterface_iid FILE "macos.json")
+
+public:
+ QMouseInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // MACOSMOUSEINPUTPLUGIN_H
diff --git a/src/plugins/mouseinputs/windows/CMakeLists.txt b/src/plugins/mouseinputs/windows/CMakeLists.txt
new file mode 100644
index 0000000..c981277
--- /dev/null
+++ b/src/plugins/mouseinputs/windows/CMakeLists.txt
@@ -0,0 +1,15 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_add_plugin(WindowsMouseInputPlugin
+ OUTPUT_NAME windowsmouseinput
+ PLUGIN_TYPE mouseinputs
+ SOURCES
+ windowsmouseinput.cpp windowsmouseinput.h
+ windowsmouseinputplugin.cpp windowsmouseinputplugin.h
+ LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::UniversalInput
+ Qt::UniversalInputPrivate
+)
diff --git a/src/plugins/mouseinputs/windows/windows.json b/src/plugins/mouseinputs/windows/windows.json
new file mode 100644
index 0000000..05032c1
--- /dev/null
+++ b/src/plugins/mouseinputs/windows/windows.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "windows" ]
+}
diff --git a/src/plugins/mouseinputs/windows/windowsmouseinput.cpp b/src/plugins/mouseinputs/windows/windowsmouseinput.cpp
new file mode 100644
index 0000000..d6b86d9
--- /dev/null
+++ b/src/plugins/mouseinputs/windows/windowsmouseinput.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.cpp" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#include "windowsmouseinput.h"
+#include <QtUniversalInput/QUniversalInput>
+#include <QtEndian>
+
+#include <QCursor>
+#include <QGuiApplication>
+#include <QWindow>
+
+QT_BEGIN_NAMESPACE
+
+static WindowsMouseInput *self = nullptr;
+
+WindowsMouseInput::WindowsMouseInput()
+{
+ self = this;
+
+ m_timer = new QTimer(this);
+ m_timer->setInterval(1);
+ connect(m_timer, &QTimer::timeout, this, &WindowsMouseInput::onUpdate);
+ m_timer->start();
+}
+
+WindowsMouseInput::~WindowsMouseInput()
+{
+ self = nullptr;
+ m_timer->stop();
+ delete m_timer;
+}
+
+QVector2D WindowsMouseInput::getMouseDelta()
+{
+ // kind of a hack, but it works for now
+ static QVector2D previousLocation;
+ auto pos = QCursor::pos();
+ QVector2D currentLocation = {float(pos.x()), float(pos.y())};
+ QVector2D delta = currentLocation - previousLocation;
+ previousLocation = currentLocation;
+ return delta;
+}
+
+void WindowsMouseInput::setCursorCenterOfWindow()
+{
+ QWindow* window = QGuiApplication::focusWindow();
+ if (!window)
+ return;
+ QPoint center = window->geometry().center();
+ QCursor::setPos(center);
+}
+
+void WindowsMouseInput::onUpdate()
+{
+ if (!self)
+ return;
+
+ auto input = QUniversalInput::instance();
+
+ QVector2D delta = getMouseDelta();
+ if (delta.x() == 0 && delta.y() == 0) {
+ return;
+ }
+
+ input->mouseMove(delta);
+
+ if (input->isMouseDisabled()) {
+ setCursorCenterOfWindow();
+ // set cursor to blank
+ QGuiApplication::setOverrideCursor(Qt::BlankCursor);
+ m_wasDisabled = true;
+ } else if (m_wasDisabled) {
+ QGuiApplication::setOverrideCursor(Qt::ArrowCursor);
+ m_wasDisabled = false;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/mouseinputs/windows/windowsmouseinput.h b/src/plugins/mouseinputs/windows/windowsmouseinput.h
new file mode 100644
index 0000000..01b5cfa
--- /dev/null
+++ b/src/plugins/mouseinputs/windows/windowsmouseinput.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+/*
+ Originally based on code from "platform/macos/joypad_macos.h" from Godot Engine v4.0
+ Copyright (c) 2014-present Godot Engine contributors
+ Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
+*/
+
+#ifndef WINDOWSMOUSEINPUT_H
+#define WINDOWSMOUSEINPUT_H
+
+#include <QtUniversalInput/private/qmouseinput_p.h>
+
+#include <QTimer>
+#include <QVector2D>
+
+QT_BEGIN_NAMESPACE
+
+class WindowsMouseInput : public QMouseInput
+{
+ Q_OBJECT
+public:
+ WindowsMouseInput();
+ ~WindowsMouseInput();
+
+ QVector2D getMouseDelta();
+ void setCursorCenterOfWindow();
+
+
+public Q_SLOTS:
+ void onUpdate();
+
+private:
+ QTimer* m_timer = nullptr;
+ bool m_wasDisabled = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // WINDOWSMOUSEINPUT_H
diff --git a/src/plugins/mouseinputs/windows/windowsmouseinputplugin.cpp b/src/plugins/mouseinputs/windows/windowsmouseinputplugin.cpp
new file mode 100644
index 0000000..516751b
--- /dev/null
+++ b/src/plugins/mouseinputs/windows/windowsmouseinputplugin.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "windowsmouseinputplugin.h"
+#include "windowsmouseinput.h"
+
+QT_BEGIN_NAMESPACE
+
+QMouseInput *WindowsMouseInputPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+ if (key == QLatin1String("windows"))
+ return new WindowsMouseInput();
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/mouseinputs/windows/windowsmouseinputplugin.h b/src/plugins/mouseinputs/windows/windowsmouseinputplugin.h
new file mode 100644
index 0000000..a527047
--- /dev/null
+++ b/src/plugins/mouseinputs/windows/windowsmouseinputplugin.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef WINDOWSMOUSEINPUTPLUGIN_H
+#define WINDOWSMOUSEINPUTPLUGIN_H
+
+#include <QtUniversalInput/private/qmouseinputplugin_p.h>
+#include <QtUniversalInput/private/qmouseinput_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class WindowsMouseInputPlugin : public QMouseInputPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QMouseInputFactoryInterface_iid FILE "windows.json")
+
+public:
+ QMouseInput *create(const QString &key, const QStringList &paramList) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // WINDOWSMOUSEINPUTPLUGIN_H
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
deleted file mode 100644
index ac81e82..0000000
--- a/src/plugins/plugins.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS += gamepads