feat: New advanced joystick mode on radio, improvements to Companion and Simulator joystick support (#3111)
* Allow switches on the connected radio to control switches in the simulator via the joystick interface. * Merge configurable radio joystick interface from banyaszg. * Add two more 'axis' channels to companion. * Fix 6POS from USB joystick to simulator. * Reduce data size. * Fix build error on B&W radios. * Add 'Companion' button mode to work with the button logic in the Companion app. * Build Companion joystick setup layout at runtime. Change #define names for simulator joystick. Allow 15 axes and 32 buttons. Initial support for trim sliders. * Remove custom joystick button type for companion - use 3 buttons per switch in companion to control simulator switch state. * Fix joystick constructor calls parameters. Connect some radio joystick trims to simulator widgets. * Restore 'companion' button type to reduce number of buttons needed. Make trims work via buttons in USB joystick. * Connect joystick trim buttons to simulator widgets. * Use companion source names for trims. * Add encode / decode of usb joystick custom settings to Companion. * Convert to lvgl layouts. Update language files. * Fix generated YAML storage files. * Fix initialisation error when editing button channel. Cleanup code. * Fix rebase. * Fix refresh of joystick channel list view after editing an entry. Companion joystick config window uses two columns. * Fix vertical joystick inversion bug. Change stick axis labels. --------- Co-authored-by: Phil Mitchell <phil.a.mitchell@gmail.com>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "joystickdialog.h"
|
||||
#include "ui_joystickdialog.h"
|
||||
#include "eeprominterface.h"
|
||||
|
||||
#include "boards.h"
|
||||
#include "constants.h"
|
||||
@@ -33,40 +34,71 @@ joystickDialog::joystickDialog(QWidget *parent, int stick) :
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
int jscaltmp[8][3] = {
|
||||
{32767, 0, -32767},
|
||||
{32767, 0, -32767},
|
||||
{32767, 0, -32767},
|
||||
{32767, 0, -32767},
|
||||
{32767, 0, -32767},
|
||||
{32767, 0, -32767},
|
||||
{32767, 0, -32767},
|
||||
{32767, 0, -32767}
|
||||
};
|
||||
memcpy(jscal, jscaltmp, sizeof (jscal));
|
||||
int i;
|
||||
char s[20];
|
||||
|
||||
foreach(QComboBox *cb, findChildren<QComboBox *>(QRegularExpression("jsmapCB_[0-9]+"))) {
|
||||
populateSourceCombo(cb);
|
||||
sticks[cb->property("channel").toUInt()] = cb;
|
||||
}
|
||||
foreach(QCheckBox *ib, findChildren<QCheckBox *>(QRegularExpression("ChInv_[0-9]+"))) {
|
||||
invert[ib->property("channel").toUInt()] = ib;
|
||||
}
|
||||
foreach(QSlider *sl, findChildren<QSlider *>(QRegularExpression("Ch_[0-9]+"))) {
|
||||
sliders[sl->property("channel").toUInt()] = sl;
|
||||
for (i = 0; i < MAX_JS_AXES; i += 1) {
|
||||
jscal[i][0] = 32767;
|
||||
jscal[i][1] = 0;
|
||||
jscal[i][2] = -32767;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
|
||||
if (!g.joystick[i].existsOnDisk())
|
||||
continue;
|
||||
QGridLayout *grid = findChild<QGridLayout*>("gridLayout");
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
if (grid) {
|
||||
for (i = 0; i < MAX_JS_AXES; i += 1, row += 1) {
|
||||
col = (i & 1) * 4;
|
||||
sprintf(s, "Ch%d", i + 1);
|
||||
QLabel *l = new QLabel(s);
|
||||
grid->addWidget(l, row/2, col+0, 1, 1);
|
||||
QSlider *s = new QSlider(Qt::Horizontal);
|
||||
s->setMinimum(-32767);
|
||||
s->setMaximum(32767);
|
||||
sliders[row] = s;
|
||||
grid->addWidget(s, row/2, col+1, 1, 1);
|
||||
QCheckBox *c = new QCheckBox("");
|
||||
invert[row] = c;
|
||||
grid->addWidget(c, row/2, col+2, 1, 1);
|
||||
QComboBox *d = new QComboBox();
|
||||
populateSourceCombo(d);
|
||||
sticks[row] = d;
|
||||
grid->addWidget(d, row/2, col+3, 1, 1);
|
||||
}
|
||||
if (row & 1) row += 1;
|
||||
for (i = 0; i < MAX_JS_BUTTONS; i += 1, row += 1) {
|
||||
col = (i & 1) * 4;
|
||||
sprintf(s, "Btn%d", i + 1);
|
||||
QLabel *l = new QLabel(s);
|
||||
grid->addWidget(l, row/2, col+0, 1, 1);
|
||||
QSlider *s = new QSlider(Qt::Horizontal);
|
||||
s->setMinimum(0);
|
||||
s->setMaximum(1);
|
||||
sliders[row] = s;
|
||||
grid->addWidget(s, row/2, col+1, 1, 1);
|
||||
QComboBox *d = new QComboBox();
|
||||
populateButtonCombo(d);
|
||||
sticks[row] = d;
|
||||
grid->addWidget(d, row/2, col+3, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
jscal[i][0] = g.joystick[i].stick_min();
|
||||
jscal[i][1] = g.joystick[i].stick_med();
|
||||
jscal[i][2] = g.joystick[i].stick_max();
|
||||
sliders[i]->setMinimum(jscal[i][0]);
|
||||
sliders[i]->setMaximum(jscal[i][2]);
|
||||
invert[i]->setChecked(g.joystick[i].stick_inv());
|
||||
sticks[i]->setCurrentIndex(sticks[i]->findData(g.joystick[i].stick_axe()));
|
||||
for (int i = 0; i < MAX_JS_AXES; ++i) {
|
||||
if (g.joystick[i].existsOnDisk()) {
|
||||
jscal[i][0] = g.joystick[i].stick_min();
|
||||
jscal[i][1] = g.joystick[i].stick_med();
|
||||
jscal[i][2] = g.joystick[i].stick_max();
|
||||
sliders[i]->setMinimum(jscal[i][0]);
|
||||
sliders[i]->setMaximum(jscal[i][2]);
|
||||
invert[i]->setChecked(g.joystick[i].stick_inv());
|
||||
sticks[i]->setCurrentIndex(sticks[i]->findData(g.joystick[i].stick_axe()));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_JS_BUTTONS; ++i) {
|
||||
if (g.jsButton[i].existsOnDisk()) {
|
||||
sticks[i+MAX_JS_AXES]->setCurrentIndex(sticks[i+MAX_JS_AXES]->findData(g.jsButton[i].button_idx()));
|
||||
}
|
||||
}
|
||||
|
||||
ui->backButton->setEnabled(false);
|
||||
@@ -86,6 +118,7 @@ joystickDialog::joystickDialog(QWidget *parent, int stick) :
|
||||
loadStep();
|
||||
|
||||
connect(joystick, SIGNAL(axisValueChanged(int, int)), this, SLOT(onjoystickAxisValueChanged(int, int)));
|
||||
connect(joystick, SIGNAL(buttonValueChanged(int, bool)), this, SLOT(onjoystickButtonValueChanged(int, bool)));
|
||||
connect(ui->joystickCB, SIGNAL(currentIndexChanged(int)), this, SLOT(joystickOpen(int)));
|
||||
connect(ui->joystickChkB, SIGNAL(toggled(bool)), this, SLOT(joystickSetEnabled(bool)));
|
||||
}
|
||||
@@ -98,13 +131,75 @@ joystickDialog::~joystickDialog()
|
||||
void joystickDialog::populateSourceCombo(QComboBox * cb)
|
||||
{
|
||||
int i;
|
||||
QString wname;
|
||||
|
||||
Board::Type m_board = getCurrentBoard();
|
||||
GeneralSettings radioSettings = GeneralSettings();
|
||||
|
||||
int ttlSticks = Boards::getCapability(m_board, Board::Sticks);
|
||||
int ttlKnobs = Boards::getCapability(m_board, Board::Pots);
|
||||
int ttlFaders = Boards::getCapability(m_board, Board::Sliders);
|
||||
|
||||
cb->clear();
|
||||
cb->addItem(tr("Not Assigned"), -1);
|
||||
for (i=0; i < Board::STICK_AXIS_COUNT; ++i) {
|
||||
cb->addItem(Boards::getAxisName(i) % tr(" Stick"), i);
|
||||
|
||||
for (i=0; i < ttlSticks; ++i) {
|
||||
wname = Boards::getAxisName(i);
|
||||
cb->addItem(wname, i);
|
||||
}
|
||||
for (; i < Board::STICK_AXIS_COUNT + CPN_MAX_POTS + CPN_MAX_SLIDERS; ++i) {
|
||||
cb->addItem(tr("Knob/Slider %1").arg(i - Board::STICK_AXIS_COUNT + 1), i);
|
||||
|
||||
for (i = 0; i < ttlKnobs; ++i) {
|
||||
if (radioSettings.isPotAvailable(i)) {
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_STICK, ttlSticks + i).toString(nullptr, &radioSettings);
|
||||
cb->addItem(wname, i + ttlSticks);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ttlFaders; ++i) {
|
||||
if (radioSettings.isSliderAvailable(i)) {
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_STICK, ttlSticks + ttlKnobs + i).toString(nullptr, &radioSettings);
|
||||
cb->addItem(wname, i + ttlSticks + ttlKnobs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void joystickDialog::populateButtonCombo(QComboBox * cb)
|
||||
{
|
||||
int i;
|
||||
QString wname;
|
||||
|
||||
Board::Type m_board = getCurrentBoard();
|
||||
GeneralSettings radioSettings = GeneralSettings();
|
||||
|
||||
int ttlSwitches = Boards::getCapability(m_board, Board::Switches);
|
||||
int ttlTrims = Boards::getCapability(m_board, Board::NumTrims);
|
||||
|
||||
cb->clear();
|
||||
cb->addItem(tr("Not Assigned"), -1);
|
||||
|
||||
Board::SwitchType swcfg;
|
||||
for (i = 0; i < ttlSwitches; ++i) {
|
||||
if (radioSettings.switchConfig[i] != Board::SWITCH_NOT_AVAILABLE) {
|
||||
swcfg = Board::SwitchType(radioSettings.switchConfig[i]);
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_SWITCH, i).toString(nullptr, &radioSettings);
|
||||
if (swcfg == Board::SWITCH_3POS) {
|
||||
cb->addItem(wname + CPN_STR_SW_INDICATOR_UP, i | JS_BUTTON_3POS_UP);
|
||||
cb->addItem(wname + CPN_STR_SW_INDICATOR_DN, i | JS_BUTTON_3POS_DN);
|
||||
} else {
|
||||
cb->addItem(wname, i | JS_BUTTON_TOGGLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ttlTrims; i += 1) {
|
||||
wname = RawSource(RawSourceType::SOURCE_TYPE_TRIM, i).toString(nullptr, &radioSettings);
|
||||
if ((i == 0) || (i == 3)) {
|
||||
cb->addItem(wname + " Left", i + ttlSwitches | JS_BUTTON_3POS_DN);
|
||||
cb->addItem(wname + " Right", i + ttlSwitches | JS_BUTTON_3POS_UP);
|
||||
} else {
|
||||
cb->addItem(wname + " Down", i + ttlSwitches | JS_BUTTON_3POS_DN);
|
||||
cb->addItem(wname + " Up", i + ttlSwitches | JS_BUTTON_3POS_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +210,7 @@ bool joystickDialog::loadJoysticks(int stick)
|
||||
|
||||
ui->joystickCB->clear();
|
||||
joystickNames << tr("No joysticks found");
|
||||
joystick = new Joystick(0, false, 0, 0);
|
||||
joystick = new Joystick(0, 0, false, 0);
|
||||
if ( joystick ) {
|
||||
if ( joystick->joystickNames.count() > 0 ) {
|
||||
joystickNames = joystick->joystickNames;
|
||||
@@ -140,10 +235,10 @@ void joystickDialog::joystickOpen(int stick)
|
||||
|
||||
joystick = new Joystick(this, 1, false, 0);
|
||||
if (joystick && joystick->open(stick)) {
|
||||
numAxes = std::min(joystick->numAxes, MAX_JOYSTICKS);
|
||||
numAxes = std::min(joystick->numAxes, MAX_JS_AXES);
|
||||
for (int j=0; j<numAxes; j++) {
|
||||
joystick->sensitivities[j] = 0;
|
||||
joystick->deadzones[j]=20;
|
||||
joystick->deadzones[j] = 20;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -161,7 +256,7 @@ void joystickDialog::joystickSetEnabled(bool enable)
|
||||
|
||||
void joystickDialog::onjoystickAxisValueChanged(int axis, int value)
|
||||
{
|
||||
if (axis >= MAX_JOYSTICKS)
|
||||
if (axis >= MAX_JS_AXES)
|
||||
return;
|
||||
|
||||
if (started) {
|
||||
@@ -177,6 +272,14 @@ void joystickDialog::onjoystickAxisValueChanged(int axis, int value)
|
||||
sliders[axis]->setValue(value);
|
||||
}
|
||||
|
||||
void joystickDialog::onjoystickButtonValueChanged(int button, bool state)
|
||||
{
|
||||
if (button >= MAX_JS_BUTTONS)
|
||||
return;
|
||||
|
||||
sliders[button + MAX_JS_AXES]->setValue(state);
|
||||
}
|
||||
|
||||
void joystickDialog::loadStep()
|
||||
{
|
||||
switch (step) {
|
||||
@@ -241,8 +344,6 @@ void joystickDialog::on_cancelButton_clicked()
|
||||
|
||||
void joystickDialog::on_okButton_clicked()
|
||||
{
|
||||
int stick;
|
||||
|
||||
g.jsSupport(ui->joystickChkB->isChecked());
|
||||
g.jsCtrl(ui->joystickCB->currentIndex());
|
||||
|
||||
@@ -261,8 +362,8 @@ void joystickDialog::on_okButton_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
|
||||
stick = sticks[i]->currentData().toInt();
|
||||
for (int i = 0; i < MAX_JS_AXES; ++i) {
|
||||
auto stick = sticks[i]->currentData().toInt();
|
||||
if (stick < 0) {
|
||||
g.joystick[i].stick_axe(-1);
|
||||
}
|
||||
@@ -276,5 +377,16 @@ void joystickDialog::on_okButton_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_JS_BUTTONS; ++i) {
|
||||
auto btn = sticks[i+MAX_JS_AXES]->currentData().toInt();
|
||||
if (btn < 0) {
|
||||
g.jsButton[i].button_idx(-1);
|
||||
}
|
||||
else {
|
||||
g.jsButton[i].button_idx(btn);
|
||||
qDebug() << "joystick button mapping " << sticks[i+MAX_JS_AXES]->objectName() << "stick:" << i << "idx:" << btn;
|
||||
}
|
||||
}
|
||||
|
||||
this->accept();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user