aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
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