Joystick: SDL & Android cleanup

This commit is contained in:
Holden
2024-12-15 04:58:50 -05:00
committed by Holden
parent f1fb8e057d
commit fd9e451a2b
14 changed files with 326 additions and 329 deletions
+1 -1
View File
@@ -20,5 +20,5 @@
path = libs/qmdnsengine
url = https://github.com/nitroshare/qmdnsengine.git
[submodule "resources/SDL_GameControllerDB"]
path = resources/SDL_GameControllerDB
path = libs/SDL_GameControllerDB
url = https://github.com/mdqinc/SDL_GameControllerDB.git
-3
View File
@@ -100,9 +100,6 @@
<file alias="radioYawLeft.png">resources/calibration/mode2/radioYawLeft.png</file>
<file alias="radioYawRight.png">resources/calibration/mode2/radioYawRight.png</file>
</qresource>
<qresource prefix="/db/mapping/joystick">
<file alias="gamecontrollerdb.txt">resources/SDL_GameControllerDB/gamecontrollerdb.txt</file>
</qresource>
<qresource prefix="/res/audio">
<file alias="Alert">resources/audio/alert.wav</file>
</qresource>
+3 -7
View File
@@ -40,7 +40,7 @@ target_sources(Joystick
JoystickSDL.h
)
target_compile_definitions(Joystick PRIVATE QGC_SDL_JOYSTICK)
target_compile_definitions(Joystick PRIVATE QGC_SDL_JOYSTICK SDL_MAIN_HANDLED)
FetchContent_Declare(SDL_GameControllerDB
GIT_REPOSITORY https://github.com/mdqinc/SDL_GameControllerDB.git
@@ -62,14 +62,10 @@ qt_add_resources(Joystick "gamecontrollerdb.txt"
qt_add_qml_module(Joystick
URI QGroundControl.JoystickManager
VERSION 1.0
OUTPUT_TARGETS Joystick_targets
IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY}
)
cmake_print_variables(Joystick_targets)
set(MINIMUM_SDL2_VERSION 2.30.0)
set(MINIMUM_SDL2_VERSION 2.24.0)
if(NOT QGC_BUILD_DEPENDENCIES)
find_package(SDL2 ${MINIMUM_SDL2_VERSION})
if(TARGET SDL2::SDL2)
@@ -93,7 +89,7 @@ message(STATUS "Building JoystickSDL")
include(FetchContent)
FetchContent_Declare(SDL
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
GIT_TAG release-2.30.7
GIT_TAG release-2.30.10
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
+4 -3
View File
@@ -23,7 +23,7 @@
#include <QtCore/QSettings>
#include <QtCore/QThread>
// JoystickLog Category declaration moved to QGCLoggingCategory.cc to allow access in Vehicle
QGC_LOGGING_CATEGORY(JoystickLog, "Joystick")
QGC_LOGGING_CATEGORY(JoystickValuesLog, "JoystickValuesLog")
int Joystick::_transmitterMode = 2;
@@ -41,8 +41,9 @@ AssignableButtonAction::AssignableButtonAction(QObject* parent, QString action_,
{
}
Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatCount)
: _name (name)
Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, QObject *parent)
: QThread(parent)
, _name (name)
, _axisCount (axisCount)
, _buttonCount (buttonCount)
, _hatCount (hatCount)
+2 -2
View File
@@ -22,7 +22,7 @@
#include <QtCore/QLoggingCategory>
#include <QtQmlIntegration/QtQmlIntegration>
// JoystickLog Category declaration moved to QGCLoggingCategory.cc to allow access in Vehicle
Q_DECLARE_LOGGING_CATEGORY(JoystickLog)
Q_DECLARE_LOGGING_CATEGORY(JoystickValuesLog)
Q_DECLARE_METATYPE(GRIPPER_ACTIONS)
@@ -62,7 +62,7 @@ class Joystick : public QThread
Q_MOC_INCLUDE("QmlObjectListModel.h")
Q_MOC_INCLUDE("Vehicle.h")
public:
Joystick(const QString& name, int axisCount, int buttonCount, int hatCount);
Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, QObject *parent = nullptr);
virtual ~Joystick();
+134 -157
View File
@@ -9,49 +9,39 @@
#include "JoystickAndroid.h"
#include "JoystickManager.h"
#include "AndroidInterface.h"
#include "QGCLoggingCategory.h"
#include <QtCore/QJniEnvironment>
#include <QtCore/QJniObject>
int JoystickAndroid::_androidBtnListCount;
int *JoystickAndroid::_androidBtnList;
int JoystickAndroid::ACTION_DOWN;
int JoystickAndroid::ACTION_UP;
int JoystickAndroid::AXIS_HAT_X;
int JoystickAndroid::AXIS_HAT_Y;
QMutex JoystickAndroid::m_mutex;
QGC_LOGGING_CATEGORY(JoystickAndroidLog, "qgc.joystick.joystickandroid")
static void clear_jni_exception()
{
QJniEnvironment jniEnv;
if (jniEnv->ExceptionCheck()) {
jniEnv->ExceptionDescribe();
jniEnv->ExceptionClear();
}
}
QList<int> JoystickAndroid::_androidBtnList(_androidBtnListCount);
int JoystickAndroid::ACTION_DOWN = 0;
int JoystickAndroid::ACTION_UP = 0;
int JoystickAndroid::AXIS_HAT_X = 0;
int JoystickAndroid::AXIS_HAT_Y = 0;
QMutex JoystickAndroid::_mutex;
JoystickAndroid::JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id)
: Joystick(name,axisCount,buttonCount,0)
JoystickAndroid::JoystickAndroid(const QString &name, int axisCount, int buttonCount, int id, QObject *parent)
: Joystick(name, axisCount, buttonCount, 0, parent)
, deviceId(id)
{
int i;
btnCode.resize(_buttonCount);
axisCode.resize(_axisCount);
btnValue.resize(_buttonCount);
axisValue.resize(_axisCount);
QJniEnvironment env;
QJniObject inputDevice = QJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", id);
//set button mapping (number->code)
jintArray b = env->NewIntArray(_androidBtnListCount);
env->SetIntArrayRegion(b,0,_androidBtnListCount,_androidBtnList);
QJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", b);
jbooleanArray jSupportedButtons = btns.object<jbooleanArray>();
jboolean* supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr);
//create a mapping table (btnCode) that maps button number with button code
btnValue = new bool[_buttonCount];
btnCode = new int[_buttonCount];
const jintArray btnArr = env->NewIntArray(_androidBtnListCount);
env->SetIntArrayRegion(btnArr, 0, _androidBtnListCount, _androidBtnList.constData());
const QJniObject inputDevice = QJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", id);
const QJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", btnArr);
const jbooleanArray jSupportedButtons = btns.object<jbooleanArray>();
jboolean *const supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr);
int c = 0;
for (i = 0; i < _androidBtnListCount; i++) {
for (int i = 0; i < _androidBtnListCount; i++) {
if (supportedButtons[i]) {
btnValue[c] = false;
btnCode[c] = _androidBtnList[i];
@@ -61,14 +51,10 @@ JoystickAndroid::JoystickAndroid(const QString& name, int axisCount, int buttonC
env->ReleaseBooleanArrayElements(jSupportedButtons, supportedButtons, 0);
// set axis mapping (number->code)
axisValue = new int[_axisCount];
axisCode = new int[_axisCount];
QJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;");
for (i = 0; i < _axisCount; i++) {
QJniObject range = rangeListNative.callObjectMethod("get", "(I)Ljava/lang/Object;",i);
const QJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;");
for (int i = 0; i < _axisCount; i++) {
const QJniObject range = rangeListNative.callObjectMethod("get", "(I)Ljava/lang/Object;", i);
axisCode[i] = range.callMethod<jint>("getAxis");
// Don't allow two axis with the same code
for (int j = 0; j < i; j++) {
if (axisCode[i] == axisCode[j]) {
axisCode[i] = -1;
@@ -77,48 +63,44 @@ JoystickAndroid::JoystickAndroid(const QString& name, int axisCount, int buttonC
}
axisValue[i] = 0;
}
qCDebug(JoystickAndroidLog) << "axis:" << _axisCount << "buttons:" << _buttonCount;
qCDebug(JoystickLog) << "axis:" <<_axisCount << "buttons:" <<_buttonCount;
QtAndroidPrivate::registerGenericMotionEventListener(this);
QtAndroidPrivate::registerKeyEventListener(this);
}
JoystickAndroid::~JoystickAndroid() {
delete btnCode;
delete axisCode;
delete btnValue;
delete axisValue;
JoystickAndroid::~JoystickAndroid()
{
QtAndroidPrivate::unregisterGenericMotionEventListener(this);
QtAndroidPrivate::unregisterKeyEventListener(this);
}
QMap<QString, Joystick*> JoystickAndroid::discover() {
QMap<QString, Joystick*> JoystickAndroid::discover()
{
static QMap<QString, Joystick*> ret;
QMutexLocker lock(&m_mutex);
QMutexLocker lock(&_mutex);
const QJniObject object = QJniObject::callStaticObjectMethod<jintArray>("android/view/InputDevice", "getDeviceIds");
jintArray jarr = object.object<jintArray>();
QJniEnvironment env;
QJniObject o = QJniObject::callStaticObjectMethod<jintArray>("android/view/InputDevice", "getDeviceIds");
jintArray jarr = o.object<jintArray>();
int sz = env->GetArrayLength(jarr);
jint *buff = env->GetIntArrayElements(jarr, nullptr);
const int len = env->GetArrayLength(jarr);
jint *const buff = env->GetIntArrayElements(jarr, nullptr);
int SOURCE_GAMEPAD = QJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_GAMEPAD");
int SOURCE_JOYSTICK = QJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_JOYSTICK");
const int SOURCE_GAMEPAD = QJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_GAMEPAD");
const int SOURCE_JOYSTICK = QJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_JOYSTICK");
QList<QString> names;
for (int i = 0; i < len; ++i) {
const QJniObject inputDevice = QJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", buff[i]);
const int sources = inputDevice.callMethod<jint>("getSources", "()I");
if (((sources & SOURCE_GAMEPAD) != SOURCE_GAMEPAD) && ((sources & SOURCE_JOYSTICK) != SOURCE_JOYSTICK)) {
continue;
}
for (int i = 0; i < sz; ++i) {
QJniObject inputDevice = QJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", buff[i]);
int sources = inputDevice.callMethod<jint>("getSources", "()I");
if (((sources & SOURCE_GAMEPAD) != SOURCE_GAMEPAD) //check if the input device is interesting to us
&& ((sources & SOURCE_JOYSTICK) != SOURCE_JOYSTICK)) continue;
// get id and name
QString id = inputDevice.callObjectMethod("getDescriptor", "()Ljava/lang/String;").toString();
QString name = inputDevice.callObjectMethod("getName", "()Ljava/lang/String;").toString();
const QString id = inputDevice.callObjectMethod("getDescriptor", "()Ljava/lang/String;").toString();
const QString name = inputDevice.callObjectMethod("getName", "()Ljava/lang/String;").toString();
names.push_back(name);
@@ -126,22 +108,23 @@ QMap<QString, Joystick*> JoystickAndroid::discover() {
continue;
}
// get number of axis
QJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;");
int axisCount = rangeListNative.callMethod<jint>("size");
const QJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;");
const int axisCount = rangeListNative.callMethod<jint>("size");
// get number of buttons
jintArray a = env->NewIntArray(_androidBtnListCount);
env->SetIntArrayRegion(a,0,_androidBtnListCount,_androidBtnList);
QJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", a);
jbooleanArray jSupportedButtons = btns.object<jbooleanArray>();
jboolean* supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr);
jintArray arr = env->NewIntArray(_androidBtnListCount);
env->SetIntArrayRegion(arr, 0, _androidBtnListCount, _androidBtnList.constData());
const QJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", arr);
const jbooleanArray jSupportedButtons = btns.object<jbooleanArray>();
jboolean *const supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr);
int buttonCount = 0;
for (int j=0;j<_androidBtnListCount;j++)
if (supportedButtons[j]) buttonCount++;
for (int j = 0; j < _androidBtnListCount; j++) {
if (supportedButtons[j]) {
buttonCount++;
}
}
env->ReleaseBooleanArrayElements(jSupportedButtons, supportedButtons, 0);
qCDebug(JoystickLog) << "\t" << name << "id:" << buff[i] << "axes:" << axisCount << "buttons:" << buttonCount;
qCDebug(JoystickAndroidLog) << name << "id:" << buff[i] << "axes:" << axisCount << "buttons:" << buttonCount;
ret[name] = new JoystickAndroid(name, axisCount, buttonCount, buff[i]);
}
@@ -159,104 +142,98 @@ QMap<QString, Joystick*> JoystickAndroid::discover() {
return ret;
}
bool JoystickAndroid::handleKeyEvent(jobject event)
{
QMutexLocker lock(&_mutex);
bool JoystickAndroid::handleKeyEvent(jobject event) {
QJniObject ev(event);
QMutexLocker lock(&m_mutex);
const int _deviceId = ev.callMethod<jint>("getDeviceId", "()I");
if (_deviceId!=deviceId) return false;
if (_deviceId != deviceId) {
return false;
}
const int action = ev.callMethod<jint>("getAction", "()I");
const int keyCode = ev.callMethod<jint>("getKeyCode", "()I");
for (int i = 0; i <_buttonCount; i++) {
if (btnCode[i] == keyCode) {
if (action == ACTION_DOWN) btnValue[i] = true;
if (action == ACTION_UP) btnValue[i] = false;
return true;
for (int i = 0; i < _buttonCount; i++) {
if (btnCode[i] != keyCode) {
continue;
}
if (action == ACTION_DOWN) {
btnValue[i] = true;
} else if (action == ACTION_UP) {
btnValue[i] = false;
}
return true;
}
return false;
}
bool JoystickAndroid::handleGenericMotionEvent(jobject event) {
QJniObject ev(event);
QMutexLocker lock(&m_mutex);
const int _deviceId = ev.callMethod<jint>("getDeviceId", "()I");
if (_deviceId!=deviceId) return false;
for (int i = 0; i <_axisCount; i++) {
const float v = ev.callMethod<jfloat>("getAxisValue", "(I)F",axisCode[i]);
axisValue[i] = static_cast<int>((v*32767.f));
}
return true;
}
bool JoystickAndroid::_open(void) {
return true;
}
void JoystickAndroid::_close(void) {
}
bool JoystickAndroid::_update(void)
bool JoystickAndroid::handleGenericMotionEvent(jobject event)
{
QMutexLocker lock(&_mutex);
QJniObject ev(event);
const int _deviceId = ev.callMethod<jint>("getDeviceId", "()I");
if (_deviceId != deviceId) {
return false;
}
for (int i = 0; i < _axisCount; i++) {
const float v = ev.callMethod<jfloat>("getAxisValue", "(I)F", axisCode[i]);
axisValue[i] = static_cast<int>(v * 32767.f);
}
return true;
}
bool JoystickAndroid::_getButton(int i) {
return btnValue[ i ];
}
int JoystickAndroid::_getAxis(int i) {
return axisValue[ i ];
}
int JoystickAndroid::_getAndroidHatAxis(int axisHatCode) {
for(int i = 0; i < _axisCount; i++) {
int JoystickAndroid::_getAndroidHatAxis(int axisHatCode)
{
for (int i = 0; i < _axisCount; i++) {
if (axisCode[i] == axisHatCode) {
return _getAxis(i);
}
}
return 0;
}
bool JoystickAndroid::_getHat(int hat,int i) {
bool JoystickAndroid::_getHat(int hat, int i)
{
// Android supports only one hat button
if (hat != 0) {
return false;
}
switch (i) {
case 0:
return _getAndroidHatAxis(AXIS_HAT_Y) < 0;
case 1:
return _getAndroidHatAxis(AXIS_HAT_Y) > 0;
case 2:
return _getAndroidHatAxis(AXIS_HAT_X) < 0;
case 3:
return _getAndroidHatAxis(AXIS_HAT_X) > 0;
default:
return false;
case 0:
return (_getAndroidHatAxis(AXIS_HAT_Y) < 0);
case 1:
return (_getAndroidHatAxis(AXIS_HAT_Y) > 0);
case 2:
return (_getAndroidHatAxis(AXIS_HAT_X) < 0);
case 3:
return (_getAndroidHatAxis(AXIS_HAT_X) > 0);
default:
return false;
}
}
//helper method
bool JoystickAndroid::init() {
//this gets list of all possible buttons - this is needed to check how many buttons our gamepad supports
//instead of the whole logic below we could have just a simple array of hardcoded int values as these 'should' not change
bool JoystickAndroid::init()
{
static QList<int> ret(_androidBtnListCount);
//int JoystickAndroid::_androidBtnListCount;
_androidBtnListCount = 31;
static int ret[31]; //there are 31 buttons in total accordingy to the API
int i;
//int *JoystickAndroid::
_androidBtnList = ret;
clear_jni_exception();
(void) AndroidInterface::cleanJavaException();
int i;
for (i = 1; i <= 16; i++) {
QString name = "KEYCODE_BUTTON_"+QString::number(i);
ret[i-1] = QJniObject::getStaticField<jint>("android/view/KeyEvent", name.toStdString().c_str());
const QString name = QStringLiteral("KEYCODE_BUTTON_") + QString::number(i);
ret[i - 1] = QJniObject::getStaticField<jint>("android/view/KeyEvent", name.toStdString().c_str());
}
i--;
@@ -284,41 +261,41 @@ bool JoystickAndroid::init() {
return true;
}
static const char kJniClassName[] {"org/mavlink/qgroundcontrol/QGCUsbSerialManager"};
static void jniUpdateAvailableJoysticks(JNIEnv *envA, jobject thizA)
{
Q_UNUSED(envA);
Q_UNUSED(thizA);
Q_UNUSED(envA); Q_UNUSED(thizA);
qCDebug(JoystickAndroidLog) << "jniUpdateAvailableJoysticks triggered";
qCDebug(JoystickLog) << "jniUpdateAvailableJoysticks triggered";
emit JoystickManager::instance()->updateAvailableJoysticksSignal();
}
void JoystickAndroid::setNativeMethods()
{
qCDebug(JoystickLog) << "Registering Native Functions";
qCDebug(JoystickAndroidLog) << "Registering Native Functions";
// REGISTER THE C++ FUNCTION WITH JNI
JNINativeMethod javaMethods[] {
{"nativeUpdateAvailableJoysticks", "()V", reinterpret_cast<void *>(jniUpdateAvailableJoysticks)}
static const JNINativeMethod javaMethods[] {
{"nativeUpdateAvailableJoysticks", "()V", reinterpret_cast<void*>(jniUpdateAvailableJoysticks)}
};
clear_jni_exception();
static constexpr const char *kJniClassName = "org/mavlink/qgroundcontrol/QGCUsbSerialManager";
(void) AndroidInterface::cleanJavaException();
QJniEnvironment jniEnv;
jclass objectClass = jniEnv->FindClass(kJniClassName);
if(!objectClass) {
clear_jni_exception();
qWarning() << "Couldn't find class:" << kJniClassName;
if (!objectClass) {
(void) AndroidInterface::cleanJavaException();
qCWarning(JoystickAndroidLog) << "Couldn't find class:" << kJniClassName;
return;
}
jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0]));
const jint val = jniEnv->RegisterNatives(objectClass, javaMethods, std::size(javaMethods));
if (val < 0) {
qWarning() << "Error registering methods: " << val;
qCWarning(JoystickAndroidLog) << "Error registering methods:" << val;
} else {
qCDebug(JoystickLog) << "Native Functions Registered";
qCDebug(JoystickAndroidLog) << "Native Functions Registered";
}
clear_jni_exception();
(void) AndroidInterface::cleanJavaException();
}
+24 -21
View File
@@ -9,44 +9,47 @@
#pragma once
#include "Joystick.h"
#include <QtCore/QLoggingCategory>
#include <QtCore/private/qandroidextras_p.h>
#include "Joystick.h"
Q_DECLARE_LOGGING_CATEGORY(JoystickAndroidLog)
class JoystickAndroid : public Joystick, public QtAndroidPrivate::GenericMotionEventListener, public QtAndroidPrivate::KeyEventListener
{
public:
JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id);
JoystickAndroid(const QString &name, int axisCount, int buttonCount, int id, QObject *parent = nullptr);
~JoystickAndroid();
static bool init();
static void setNativeMethods();
static QMap<QString, Joystick*> discover();
private:
bool _open() final { return true; }
void _close() final {}
bool _update() final { return true; }
bool _getButton(int i) final { return btnValue[i]; }
int _getAxis(int i) final { return axisValue[i]; }
bool _getHat(int hat, int i) final;
int _getAndroidHatAxis(int axisHatCode);
bool handleKeyEvent(jobject event);
bool handleGenericMotionEvent(jobject event);
int _getAndroidHatAxis(int axisHatCode);
virtual bool _open ();
virtual void _close ();
virtual bool _update ();
int deviceId = 0;
virtual bool _getButton (int i);
virtual int _getAxis (int i);
virtual bool _getHat (int hat,int i);
QList<int> btnCode;
QList<int> axisCode;
QList<bool> btnValue;
QList<int> axisValue;
int *btnCode;
int *axisCode;
bool *btnValue;
int *axisValue;
static int * _androidBtnList; //list of all possible android buttons
static int _androidBtnListCount;
static constexpr int _androidBtnListCount = 31;
static QList<int> _androidBtnList;
static int ACTION_DOWN, ACTION_UP, AXIS_HAT_X, AXIS_HAT_Y;
static QMutex m_mutex;
int deviceId;
static QMutex _mutex;
};
+8 -5
View File
@@ -11,10 +11,10 @@
#include "Joystick.h"
#if defined(QGC_SDL_JOYSTICK)
#include "JoystickSDL.h"
#include <SDL.h>
#elif defined(Q_OS_ANDROID)
#include "JoystickAndroid.h"
#endif
#include "MultiVehicleManager.h"
#include "QGCLoggingCategory.h"
#include <QtCore/qapplicationstatic.h>
@@ -33,7 +33,7 @@ JoystickManager::JoystickManager(QObject *parent)
{
// qCDebug(JoystickManagerLog) << Q_FUNC_INFO << this;
_joystickCheckTimer->setInterval(1000);
_joystickCheckTimer->setInterval(kTimerInterval);
_joystickCheckTimer->setSingleShot(false);
}
@@ -69,6 +69,7 @@ void JoystickManager::init()
_joystickCheckTimer->start();
});
#endif
(void) connect(_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks);
_joystickCheckTimerCounter = 5;
_joystickCheckTimer->start();
@@ -76,7 +77,7 @@ void JoystickManager::init()
void JoystickManager::_setActiveJoystickFromSettings()
{
QMap<QString,Joystick*> newMap;
QMap<QString, Joystick*> newMap;
#ifdef QGC_SDL_JOYSTICK
newMap = JoystickSDL::discover();
@@ -95,7 +96,7 @@ void JoystickManager::_setActiveJoystickFromSettings()
if (!newMap.contains(it->first)) {
qCDebug(JoystickManagerLog) << "Releasing joystick:" << it->first;
it->second->stopPolling();
it->second->wait(1000);
(void) it->second->wait(kTimeout);
it->second->deleteLater();
}
}
@@ -144,7 +145,7 @@ void JoystickManager::setActiveJoystick(Joystick *joystick)
_activeJoystick = joystick;
if (_activeJoystick != nullptr) {
if (_activeJoystick) {
qCDebug(JoystickManagerLog) << "Set active:" << _activeJoystick->name();
QSettings settings;
@@ -192,10 +193,12 @@ void JoystickManager::_updateAvailableJoysticks()
case SDL_QUIT:
qCDebug(JoystickManagerLog) << "SDL ERROR:" << SDL_GetError();
break;
case SDL_CONTROLLERDEVICEADDED:
case SDL_JOYDEVICEADDED:
qCDebug(JoystickManagerLog) << "Joystick added:" << event.jdevice.which;
_setActiveJoystickFromSettings();
break;
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_JOYDEVICEREMOVED:
qCDebug(JoystickManagerLog) << "Joystick removed:" << event.jdevice.which;
_setActiveJoystickFromSettings();
+2 -1
View File
@@ -17,7 +17,6 @@
Q_DECLARE_LOGGING_CATEGORY(JoystickManagerLog)
class Joystick;
class MultiVehicleManager;
class QTimer;
class JoystickManager : public QObject
@@ -68,6 +67,8 @@ private:
int _joystickCheckTimerCounter = 0;;
QTimer *_joystickCheckTimer = nullptr;
static constexpr int kTimerInterval = 1000;
static constexpr int kTimeout = 1000;
static constexpr const char *_settingsGroup = "JoystickManager";
static constexpr const char *_settingsKeyActiveJoystick = "ActiveJoystick";
};
+123 -99
View File
@@ -8,184 +8,208 @@
****************************************************************************/
#include "JoystickSDL.h"
#include "MultiVehicleManager.h"
#include "QGCLoggingCategory.h"
#include <QtCore/QTextStream>
#include <QtCore/QFile>
#include <QtCore/QIODevice>
#include <QtCore/QTextStream>
JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController)
: Joystick(name,axisCount,buttonCount,hatCount)
#include <SDL.h>
QGC_LOGGING_CATEGORY(JoystickSDLLog, "qgc.joystick.joysticksdl")
JoystickSDL::JoystickSDL(const QString &name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, QObject *parent)
: Joystick(name, axisCount, buttonCount, hatCount, parent)
, _isGameController(isGameController)
, _index(index)
{
// qCDebug(JoystickLog) << Q_FUNC_INFO << this;
// qCDebug(JoystickSDLLog) << Q_FUNC_INFO << this;
if(_isGameController) _setDefaultCalibration();
if (_isGameController) {
_setDefaultCalibration();
}
}
JoystickSDL::~JoystickSDL()
{
// qCDebug(JoystickLog) << Q_FUNC_INFO << this;
// qCDebug(JoystickSDLLog) << Q_FUNC_INFO << this;
}
bool JoystickSDL::init(void) {
bool JoystickSDL::init()
{
SDL_SetMainReady();
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK) < 0) {
SDL_JoystickEventState(SDL_DISABLE);
qCWarning(JoystickLog) << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError();
(void) SDL_JoystickEventState(SDL_DISABLE);
qCWarning(JoystickSDLLog) << "Failed to initialize SDL:" << SDL_GetError();
return false;
}
_loadGameControllerMappings();
return true;
}
QMap<QString, Joystick*> JoystickSDL::discover() {
QMap<QString, Joystick*> JoystickSDL::discover()
{
static QMap<QString, Joystick*> ret;
QMap<QString,Joystick*> newRet;
QMap<QString, Joystick*> newRet;
// Load available joysticks
qCDebug(JoystickSDLLog) << "Discovering joysticks";
qCDebug(JoystickLog) << "Available joysticks";
for (int i=0; i<SDL_NumJoysticks(); i++) {
for (int i = 0; i < SDL_NumJoysticks(); i++) {
QString name = SDL_JoystickNameForIndex(i);
if (!ret.contains(name)) {
int axisCount, buttonCount, hatCount;
bool isGameController = SDL_IsGameController(i);
if (SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i)) {
SDL_ClearError();
axisCount = SDL_JoystickNumAxes(sdlJoystick);
buttonCount = SDL_JoystickNumButtons(sdlJoystick);
hatCount = SDL_JoystickNumHats(sdlJoystick);
if (axisCount < 0 || buttonCount < 0 || hatCount < 0) {
qCWarning(JoystickLog) << "\t libsdl error parsing joystick features:" << SDL_GetError();
}
SDL_JoystickClose(sdlJoystick);
} else {
qCWarning(JoystickLog) << "\t libsdl failed opening joystick" << qPrintable(name) << "error:" << SDL_GetError();
continue;
}
qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController;
// Check for joysticks with duplicate names and differentiate the keys when necessary.
// This is required when using an Xbox 360 wireless receiver that always identifies as
// 4 individual joysticks, regardless of how many joysticks are actually connected to the
// receiver. Using GUID does not help, all of these devices present the same GUID.
QString originalName = name;
uint8_t duplicateIdx = 1;
while (newRet[name]) {
name = QString("%1 %2").arg(originalName).arg(duplicateIdx++);
}
newRet[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController);
} else {
if (ret.contains(name)) {
newRet[name] = ret[name];
JoystickSDL *j = static_cast<JoystickSDL*>(newRet[name]);
if (j->index() != i) {
j->setIndex(i); // This joystick index has been remapped by SDL
JoystickSDL *const joystick = static_cast<JoystickSDL*>(newRet[name]);
if (joystick->index() != i) {
joystick->setIndex(i); // This joystick index has been remapped by SDL
}
// Anything left in ret after we exit the loop has been removed (unplugged) and needs to be cleaned up.
// We will handle that in JoystickManager in case the removed joystick was in use.
ret.remove(name);
qCDebug(JoystickLog) << "\tSkipping duplicate" << name;
(void) ret.remove(name);
qCDebug(JoystickSDLLog) << "Skipping duplicate" << name;
continue;
}
SDL_Joystick *const sdlJoystick = SDL_JoystickOpen(i);
if (!sdlJoystick) {
qCWarning(JoystickSDLLog) << "SDL failed opening joystick" << qPrintable(name) << "error:" << SDL_GetError();
continue;
}
SDL_ClearError();
const int axisCount = SDL_JoystickNumAxes(sdlJoystick);
const int buttonCount = SDL_JoystickNumButtons(sdlJoystick);
const int hatCount = SDL_JoystickNumHats(sdlJoystick);
if ((axisCount < 0) || (buttonCount < 0) || (hatCount < 0)) {
qCWarning(JoystickSDLLog) << "SDL error parsing joystick features:" << SDL_GetError();
}
SDL_JoystickClose(sdlJoystick);
const bool isGameController = SDL_IsGameController(i);
qCDebug(JoystickSDLLog) << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController;
// Check for joysticks with duplicate names and differentiate the keys when necessary.
// This is required when using an Xbox 360 wireless receiver that always identifies as
// 4 individual joysticks, regardless of how many joysticks are actually connected to the
// receiver. Using GUID does not help, all of these devices present the same GUID.
const QString originalName = name;
uint8_t duplicateIdx = 1;
while (newRet[name]) {
name = QString("%1 %2").arg(originalName).arg(duplicateIdx++);
}
newRet[name] = new JoystickSDL(name, qMax(0, axisCount), qMax(0, buttonCount), qMax(0, hatCount), i, isGameController);
}
if (!newRet.count()) {
qCDebug(JoystickLog) << "\tnone found";
if (newRet.isEmpty()) {
qCDebug(JoystickSDLLog) << "None found";
}
ret = newRet;
return ret;
}
void JoystickSDL::_loadGameControllerMappings(void) {
QFile file(":/db/mapping/joystick/gamecontrollerdb.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
qWarning() << "Couldn't load GameController mapping database.";
return;
}
QTextStream s(&file);
while (!s.atEnd()) {
SDL_GameControllerAddMapping(s.readLine().toStdString().c_str());
}
}
bool JoystickSDL::_open(void) {
if ( _isGameController ) {
sdlController = SDL_GameControllerOpen(_index);
sdlJoystick = SDL_GameControllerGetJoystick(sdlController);
bool JoystickSDL::_open()
{
if (_isGameController) {
_sdlController = SDL_GameControllerOpen(_index);
_sdlJoystick = SDL_GameControllerGetJoystick(_sdlController);
} else {
sdlJoystick = SDL_JoystickOpen(_index);
_sdlJoystick = SDL_JoystickOpen(_index);
}
if (!sdlJoystick) {
qCWarning(JoystickLog) << "SDL_JoystickOpen failed:" << SDL_GetError();
if (!_sdlJoystick) {
qCWarning(JoystickSDLLog) << "SDL_JoystickOpen failed:" << SDL_GetError();
return false;
}
qCDebug(JoystickLog) << "Opened joystick at" << sdlJoystick;
qCDebug(JoystickSDLLog) << "Opened" << SDL_JoystickName(_sdlJoystick) << "joystick at" << _sdlJoystick;
return true;
}
void JoystickSDL::_close(void) {
if (sdlJoystick == nullptr) {
qCDebug(JoystickLog) << "Attempt to close null joystick!";
void JoystickSDL::_close()
{
if (!_sdlJoystick) {
qCWarning(JoystickSDLLog) << "Attempt to close null joystick!";
return;
}
qCDebug(JoystickLog) << "Closing" << SDL_JoystickName(sdlJoystick) << "at" << sdlJoystick;
qCDebug(JoystickSDLLog) << "Closing" << SDL_JoystickName(_sdlJoystick) << "joystick at" << _sdlJoystick;
if (_isGameController) {
SDL_GameControllerClose(sdlController);
SDL_GameControllerClose(_sdlController);
} else {
SDL_JoystickClose(sdlJoystick);
SDL_JoystickClose(_sdlJoystick);
}
sdlJoystick = nullptr;
sdlController = nullptr;
_sdlJoystick = nullptr;
_sdlController = nullptr;
}
bool JoystickSDL::_update(void)
bool JoystickSDL::_update()
{
if (_isGameController) {
SDL_GameControllerUpdate();
} else {
SDL_JoystickUpdate();
}
return true;
}
bool JoystickSDL::_getButton(int i) {
bool JoystickSDL::_getButton(int i)
{
int button = -1;
if (_isGameController) {
return SDL_GameControllerGetButton(sdlController, SDL_GameControllerButton(i)) == 1;
button = SDL_GameControllerGetButton(_sdlController, SDL_GameControllerButton(i));
} else {
return SDL_JoystickGetButton(sdlJoystick, i) == 1;
button = SDL_JoystickGetButton(_sdlJoystick, i);
}
return (button == 1);
}
int JoystickSDL::_getAxis(int i) {
int JoystickSDL::_getAxis(int i)
{
int axis = -1;
if (_isGameController) {
return SDL_GameControllerGetAxis(sdlController, SDL_GameControllerAxis(i));
axis = SDL_GameControllerGetAxis(_sdlController, SDL_GameControllerAxis(i));
} else {
return SDL_JoystickGetAxis(sdlJoystick, i);
axis = SDL_JoystickGetAxis(_sdlJoystick, i);
}
return axis;
}
bool JoystickSDL::_getHat(int hat, int i) {
uint8_t hatButtons[] = {SDL_HAT_UP,SDL_HAT_DOWN,SDL_HAT_LEFT,SDL_HAT_RIGHT};
if (i < int(sizeof(hatButtons))) {
return (SDL_JoystickGetHat(sdlJoystick, hat) & hatButtons[i]) != 0;
bool JoystickSDL::_getHat(int hat, int i)
{
static constexpr uint8_t hatButtons[] = {SDL_HAT_UP, SDL_HAT_DOWN, SDL_HAT_LEFT, SDL_HAT_RIGHT};
if (i >= std::size(hatButtons)) {
return false;
}
return ((SDL_JoystickGetHat(_sdlJoystick, hat) & hatButtons[i]) != 0);
}
void JoystickSDL::_loadGameControllerMappings()
{
QFile file(QStringLiteral(":/db/mapping/joystick/gamecontrollerdb.txt"));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qCWarning(JoystickSDLLog) << "Couldn't load GameController mapping database.";
return;
}
QTextStream stream(&file);
while (!stream.atEnd()) {
if (SDL_GameControllerAddMapping(stream.readLine().toStdString().c_str()) == -1) {
qCWarning(JoystickSDLLog) << "Couldn't add GameController mapping:" << SDL_GetError();
}
}
return false;
}
+25 -27
View File
@@ -7,50 +7,48 @@
*
****************************************************************************/
/// @file
/// @brief SDL Joystick Interface
#pragma once
#include <QtCore/QLoggingCategory>
#include "Joystick.h"
#define SDL_MAIN_HANDLED
struct _SDL_Joystick;
typedef struct _SDL_Joystick SDL_Joystick;
#include <SDL.h>
struct _SDL_GameController;
typedef struct _SDL_GameController SDL_GameController;
class MultiVehicleManager;
Q_DECLARE_LOGGING_CATEGORY(JoystickSDLLog)
/// @brief SDL Joystick Interface
class JoystickSDL : public Joystick
{
public:
JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController);
JoystickSDL(const QString &name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, QObject *parent = nullptr);
~JoystickSDL();
static QMap<QString, Joystick*> discover();
static bool init(void);
int index(void) const { return _index; }
int index() const { return _index; }
void setIndex(int index) { _index = index; }
// This can be uncommented to hide the calibration buttons for gamecontrollers in the future
// bool requiresCalibration(void) final { return !_isGameController; }
// bool requiresCalibration() final { return !_isGameController; }
static bool init();
static QMap<QString, Joystick*> discover();
private:
bool _open() final;
void _close() final;
bool _update() final;
bool _getButton(int i) final;
int _getAxis(int i) final;
bool _getHat(int hat, int i) final;
static void _loadGameControllerMappings();
bool _open () final;
void _close () final;
bool _update () final;
bool _getButton (int i) final;
int _getAxis (int i) final;
bool _getHat (int hat,int i) final;
SDL_Joystick* sdlJoystick;
SDL_GameController* sdlController;
bool _isGameController;
int _index; ///< Index for SDL_JoystickOpen
bool _isGameController = false;
int _index = -1;
SDL_Joystick *_sdlJoystick = nullptr;
SDL_GameController *_sdlController = nullptr;
};
-2
View File
@@ -26,8 +26,6 @@ QGC_LOGGING_CATEGORY(ParameterManagerLog, "ParameterManagerLog")
QGC_LOGGING_CATEGORY(GuidedActionsControllerLog, "GuidedActionsControllerLog")
QGC_LOGGING_CATEGORY(LocalizationLog, "LocalizationLog")
QGC_LOGGING_CATEGORY(VideoAllLog, kVideoAllLogCategory)
QGC_LOGGING_CATEGORY(JoystickLog, "JoystickLog")
QGCLoggingCategoryRegister* _instance = nullptr;
-1
View File
@@ -22,7 +22,6 @@ Q_DECLARE_LOGGING_CATEGORY(ParameterManagerLog)
Q_DECLARE_LOGGING_CATEGORY(GuidedActionsControllerLog)
Q_DECLARE_LOGGING_CATEGORY(LocalizationLog)
Q_DECLARE_LOGGING_CATEGORY(VideoAllLog) // turns on all individual QGC video logs
Q_DECLARE_LOGGING_CATEGORY(JoystickLog)
/// @def QGC_LOGGING_CATEGORY
/// This is a QGC specific replacement for Q_LOGGING_CATEGORY. It will register the category name into a