Add support for gamepad axis configuration

This commit is contained in:
Jon Beniston 2023-04-24 11:38:52 +01:00
parent 62e0ae41f3
commit fe3aeaed5b
11 changed files with 463 additions and 17 deletions

View File

@ -55,8 +55,15 @@ if(NOT SERVER_MODE)
if(Qt${QT_DEFAULT_MAJOR_VERSION}Gamepad_FOUND)
add_compile_definitions(QT_GAMEPAD_FOUND)
set(TARGET_LIB ${TARGET_LIB} Qt::Gamepad)
set(gs232controller_SOURCES ${gs232controller_SOURCES} gamepadinputcontroller.cpp)
set(gs232controller_HEADERS ${gs232controller_HEADERS} gamepadinputcontroller.h)
set(gs232controller_SOURCES
${gs232controller_SOURCES}
gamepadinputcontroller.cpp
gamepadconfigurationdialog.cpp
gamepadconfigurationdialog.ui)
set(gs232controller_HEADERS
${gs232controller_HEADERS}
gamepadinputcontroller.h
gamepadconfiguration.h)
endif()
else()

View File

@ -0,0 +1,139 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QGamepadManager>
#include <QGamepad>
#include "gamepadconfigurationdialog.h"
GamepadConfigurationDialog::GamepadConfigurationDialog(QGamepad *gamepad, QWidget* parent) :
QDialog(parent),
ui(new Ui::GamepadConfigurationDialog),
m_gamepad(gamepad)
{
ui->setupUi(this);
connect(m_gamepad, &QGamepad::axisRightXChanged, this, &GamepadConfigurationDialog::axisRightXChanged);
connect(m_gamepad, &QGamepad::axisRightYChanged, this, &GamepadConfigurationDialog::axisRightYChanged);
connect(m_gamepad, &QGamepad::axisLeftXChanged, this, &GamepadConfigurationDialog::axisLeftXChanged);
connect(m_gamepad, &QGamepad::axisLeftYChanged, this, &GamepadConfigurationDialog::axisLeftYChanged);
}
GamepadConfigurationDialog::~GamepadConfigurationDialog()
{
delete ui;
}
void GamepadConfigurationDialog::accept()
{
QDialog::accept();
}
void GamepadConfigurationDialog::on_config0_clicked()
{
if (ui->config0->text() == "Configure")
{
ui->config0->setText("Done");
ui->config1->setEnabled(false);
ui->config2->setEnabled(false);
ui->config3->setEnabled(false);
QGamepadManager::instance()->configureAxis(m_gamepad->deviceId(), QGamepadManager ::AxisRightX);
}
else
{
ui->config0->setText("Configure");
ui->config1->setEnabled(true);
ui->config2->setEnabled(true);
ui->config3->setEnabled(true);
}
}
void GamepadConfigurationDialog::on_config1_clicked()
{
if (ui->config1->text() == "Configure")
{
ui->config1->setText("Done");
ui->config0->setEnabled(false);
ui->config2->setEnabled(false);
ui->config3->setEnabled(false);
QGamepadManager::instance()->configureAxis(m_gamepad->deviceId(), QGamepadManager ::AxisRightY);
}
else
{
ui->config1->setText("Configure");
ui->config0->setEnabled(true);
ui->config2->setEnabled(true);
ui->config3->setEnabled(true);
}
}
void GamepadConfigurationDialog::on_config2_clicked()
{
if (ui->config2->text() == "Configure")
{
ui->config2->setText("Done");
ui->config0->setEnabled(false);
ui->config1->setEnabled(false);
ui->config3->setEnabled(false);
QGamepadManager::instance()->configureAxis(m_gamepad->deviceId(), QGamepadManager ::AxisLeftX);
}
else
{
ui->config2->setText("Configure");
ui->config0->setEnabled(true);
ui->config1->setEnabled(true);
ui->config3->setEnabled(true);
}
}
void GamepadConfigurationDialog::on_config3_clicked()
{
if (ui->config3->text() == "Configure")
{
ui->config3->setText("Done");
ui->config0->setEnabled(false);
ui->config1->setEnabled(false);
ui->config2->setEnabled(false);
QGamepadManager::instance()->configureAxis(m_gamepad->deviceId(), QGamepadManager ::AxisLeftY);
}
else
{
ui->config3->setText("Configure");
ui->config0->setEnabled(true);
ui->config1->setEnabled(true);
ui->config2->setEnabled(true);
}
}
void GamepadConfigurationDialog::axisRightXChanged(double value)
{
ui->value0->setText(QString::number(value));
}
void GamepadConfigurationDialog::axisRightYChanged(double value)
{
ui->value1->setText(QString::number(value));
}
void GamepadConfigurationDialog::axisLeftXChanged(double value)
{
ui->value2->setText(QString::number(value));
}
void GamepadConfigurationDialog::axisLeftYChanged(double value)
{
ui->value3->setText(QString::number(value));
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_GAMEPADCONFIGURATIONDIALOG_H
#define INCLUDE_GAMEPADCONFIGURATIONDIALOG_H
#include "ui_gamepadconfigurationdialog.h"
class QGamepad;
class GamepadConfigurationDialog : public QDialog {
Q_OBJECT
public:
explicit GamepadConfigurationDialog(QGamepad *gamepad, QWidget* parent = 0);
~GamepadConfigurationDialog();
private slots:
void accept();
void on_config0_clicked();
void on_config1_clicked();
void on_config2_clicked();
void on_config3_clicked();
void axisRightXChanged(double value);
void axisRightYChanged(double value);
void axisLeftXChanged(double value);
void axisLeftYChanged(double value);
private:
Ui::GamepadConfigurationDialog* ui;
QGamepad *m_gamepad;
};
#endif // INCLUDE_GAMEPADCONFIGURATIONDIALOG_H

View File

@ -0,0 +1,196 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GamepadConfigurationDialog</class>
<widget class="QDialog" name="GamepadConfigurationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>309</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Gamepad Configuration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QLabel" name="value0">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="config3">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="value1">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="config1">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="config3Label">
<property name="text">
<string>Azimuth Offset Axis</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="value3">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="config2">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="config2Label">
<property name="text">
<string>Elevation Offset Axis</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="config1Label">
<property name="text">
<string>Target Elevation/Y Axis</string>
</property>
</widget>
</item>
<item row="7" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="config0Label">
<property name="text">
<string>Target Aziumth/X Axis</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="value2">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="instructions">
<property name="minimumSize">
<size>
<width>0</width>
<height>80</height>
</size>
</property>
<property name="text">
<string>To configure a controller axis:
- press the Configure button,
- move the controller axis,
- then press Done</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="config0">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>config0</tabstop>
<tabstop>config1</tabstop>
<tabstop>config2</tabstop>
<tabstop>config3</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>GamepadConfigurationDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>GamepadConfigurationDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -19,6 +19,7 @@
#include <QGamepadManager>
#include "gamepadinputcontroller.h"
#include "gamepadconfigurationdialog.h"
GamepadInputController::GamepadInputController(int deviceId) :
m_gamepad(deviceId),
@ -54,6 +55,28 @@ int GamepadInputController::getNumberOfAxes() const
return 4;
}
bool GamepadInputController::supportsConfiguration() const
{
// Should only return true on Linux evdev or Android
return QGamepadManager::instance()->isConfigurationNeeded(m_gamepad.deviceId());
}
void GamepadInputController::configure()
{
disconnect(&m_gamepad, &QGamepad::axisRightXChanged, this, &GamepadInputController::axisRightXChanged);
disconnect(&m_gamepad, &QGamepad::axisRightYChanged, this, &GamepadInputController::axisRightYChanged);
disconnect(&m_gamepad, &QGamepad::axisLeftXChanged, this, &GamepadInputController::axisLeftXChanged);
disconnect(&m_gamepad, &QGamepad::axisLeftYChanged, this, &GamepadInputController::axisLeftYChanged);
GamepadConfigurationDialog dialog(&m_gamepad);
dialog.exec();
connect(&m_gamepad, &QGamepad::axisRightXChanged, this, &GamepadInputController::axisRightXChanged);
connect(&m_gamepad, &QGamepad::axisRightYChanged, this, &GamepadInputController::axisRightYChanged);
connect(&m_gamepad, &QGamepad::axisLeftXChanged, this, &GamepadInputController::axisLeftXChanged);
connect(&m_gamepad, &QGamepad::axisLeftYChanged, this, &GamepadInputController::axisLeftYChanged);
}
void GamepadInputController::axisRightXChanged(double value)
{
m_rightX = value;

View File

@ -29,6 +29,8 @@ public:
GamepadInputController(int deviceId);
double getAxisValue(int axis) override;
int getNumberOfAxes() const override;
bool supportsConfiguration() const override;
void configure() override;
static QStringList getAllControllers();
static GamepadInputController* open(const QString& name);

View File

@ -304,6 +304,8 @@ void GS232ControllerGUI::updateInputController()
ui->inputSensitivityLabel->setEnabled(enabled);
ui->inputSensitivity->setEnabled(enabled);
ui->inputSensitivityText->setEnabled(enabled);
ui->inputConfigure->setEnabled(enabled);
ui->inputConfigure->setVisible(enabled);
}
void GS232ControllerGUI::checkInputController()
@ -383,6 +385,13 @@ void GS232ControllerGUI::on_inputSensitivty_valueChanged(int value)
applySettings();
}
void GS232ControllerGUI::on_inputConfigure_clicked()
{
if (m_inputController) {
m_inputController->configure();
}
}
GS232ControllerGUI::~GS232ControllerGUI()
{
m_dfmStatusDialog.close();
@ -949,6 +958,7 @@ void GS232ControllerGUI::makeUIConnections()
QObject::connect(ui->coordinates, qOverload<int>(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_coordinates_currentIndexChanged);
QObject::connect(ui->inputController, qOverload<int>(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_inputController_currentIndexChanged);
QObject::connect(ui->inputSensitivity, qOverload<int>(&QSlider::valueChanged), this, &GS232ControllerGUI::on_inputSensitivty_valueChanged);
QObject::connect(ui->inputConfigure, &QToolButton::clicked, this, &GS232ControllerGUI::on_inputConfigure_clicked);
QObject::connect(ui->dfmTrack, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmTrack_clicked);
QObject::connect(ui->dfmLubePumps, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmLubePumps_clicked);
QObject::connect(ui->dfmBrakes, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmBrakes_clicked);

View File

@ -127,6 +127,7 @@ private slots:
void updateStatus();
void on_inputController_currentIndexChanged(int index);
void on_inputSensitivty_valueChanged(int value);
void on_inputConfigure_clicked();
void updateInputControllerList();
void checkInputController();
};

View File

@ -337,18 +337,6 @@
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QComboBox" name="inputController">
<property name="toolTip">
<string>Gamepad / joystick to use</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="azimuthMin">
<property name="maximum">
@ -742,6 +730,32 @@
</item>
</layout>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="inputController">
<property name="toolTip">
<string>Gamepad / joystick to use</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QToolButton" name="inputConfigure">
<property name="toolTip">
<string>Configure input</string>
</property>
<property name="text">
<string>C</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>

View File

@ -29,6 +29,8 @@ public:
// value returned should be current axis position in range [-1,1]
virtual double getAxisValue(int axis) = 0;
virtual int getNumberOfAxes() const = 0;
virtual bool supportsConfiguration() const { return false; }
virtual void configure() {};
};

View File

@ -5,7 +5,7 @@
The Rotator Controller feature plugin allows SDRangel to send commands to GS-232 and SPID rotators as well as hamlib's rotctld, via a serial or TCP connection.
This allows SDRangel to point antennas mounted on a rotator to a specified azimuth and elevation.
Azimuth and elevation can be set manually by a user in the GUI, via the REST API, via another plugin, such as the Map Feature, the ADS-B Demodulator, or the Star Tracker, or by controller/gamepads (such as an XBox Controller).
Azimuth and elevation can be set manually by a user in the GUI, via the REST API, via another plugin, such as the Map Feature, the ADS-B Demodulator, or the Star Tracker, or by controller/gamepads (such as an XBox Wireless Controller).
<h2>Interface</h2>
@ -109,13 +109,16 @@ Equations for translating between these coordinate systems can be found [here](h
<h3>22: Input Control</h3>
Specifies a controller/gamepad (such as an XBox Wireless Controller) that can be used to specify target coordinates or azimuth and elevation offset.
Specifies a controller/gamepad that can be used to specify target coordinates or azimuth and elevation offset.
When a controller with 2 sticks (4 axes) such as the XBox Wireless Controller is used, the right stick is used for controlling target coordinates,
When a gamepad with 2 sticks (4 axes) such as the XBox Wireless Controller is used, the right stick is used for controlling target coordinates,
while the left stick is for controlling azimuth and elevation offset.
If a controller only has 2 axes, target coordinates will be controlled when not tracking (6) and offset will be controlled when tracking.
The [Qt Gamepad](https://doc.qt.io/qt-5/qtgamepad-index.html) library is used to implement gamepad support.
On Linux, using Qt Gamepad with the evdev backend, all joysticks & gamepads appear as though they have 4 axes (a limitation of Qt Gamepad).
If using a joystick which only has 2 axes, whether it corresponds to the left or right stick can be configured by pressing the 'C' button.
On Linux, the [xone driver](https://github.com/medusalix/xone) has support for the Xbox Wireless Controller, that isn't supported by the older xpad driver that is included with Ubuntu.
<h3>23: Sensitivity</h3>