diff --git a/companion/src/apppreferencesdialog.cpp b/companion/src/apppreferencesdialog.cpp index fbac35ca4..c20001a60 100644 --- a/companion/src/apppreferencesdialog.cpp +++ b/companion/src/apppreferencesdialog.cpp @@ -273,7 +273,7 @@ void AppPreferencesDialog::initSettings() if (ui->joystickChkB->isChecked()) { QStringList joystickNames; joystickNames << tr("No joysticks found"); - joystick = new Joystick(0,false,0,0); + joystick = new Joystick(0,0,false,0); ui->joystickcalButton->setDisabled(true); ui->joystickCB->setDisabled(true); @@ -591,7 +591,7 @@ void AppPreferencesDialog::on_joystickChkB_clicked() { if (ui->joystickChkB->isChecked()) { QStringList joystickNames; joystickNames << tr("No joysticks found"); - joystick = new Joystick(0,false,0,0); + joystick = new Joystick(0,0,false,0); ui->joystickcalButton->setDisabled(true); ui->joystickCB->setDisabled(true); diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index 7791eeb4c..0724ea286 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -46,6 +46,7 @@ set(firmwares_SRCS edgetx/yaml_customfunctiondata.cpp edgetx/yaml_sensordata.cpp edgetx/yaml_screendata.cpp + edgetx/yaml_usbjoystickdata.cpp datahelpers.cpp customisation_data.cpp ) diff --git a/companion/src/firmwares/edgetx/yaml_modeldata.cpp b/companion/src/firmwares/edgetx/yaml_modeldata.cpp index e177b7631..a7e16c705 100644 --- a/companion/src/firmwares/edgetx/yaml_modeldata.cpp +++ b/companion/src/firmwares/edgetx/yaml_modeldata.cpp @@ -29,6 +29,7 @@ #include "yaml_customfunctiondata.h" #include "yaml_sensordata.h" #include "yaml_screendata.h" +#include "yaml_usbjoystickdata.h" #include "modeldata.h" #include "output_data.h" @@ -79,6 +80,12 @@ static const YamlLookupTable jitterFilterLut = { { 2, "ON" }, }; +static const YamlLookupTable usbJoystickIfModeLut = { + { 0, "JOYSTICK" }, + { 1, "GAMEPAD" }, + { 2, "MULTIAXIS" }, +}; + struct YamlTrim { int mode = 0; int ref = 0; @@ -1008,6 +1015,16 @@ Node convert::encode(const ModelData& rhs) } } + // Custom USB joytsick mapping + node["usbJoystickExtMode"] = rhs.usbJoystickExtMode; + node["usbJoystickIfMode"] = usbJoystickIfModeLut << rhs.usbJoystickIfMode; + node["usbJoystickCircularCut"] = rhs.usbJoystickCircularCut; + for (int i = 0; i < CPN_USBJ_MAX_JOYSTICK_CHANNELS; i++) { + if (rhs.usbJoystickCh[i].mode > 0) { + node["usbJoystickCh"][std::to_string(i)] = rhs.usbJoystickCh[i]; + } + } + return node; } @@ -1194,6 +1211,12 @@ bool convert::decode(const Node& node, ModelData& rhs) node["functionSwitchLogicalState"] >> rhs.functionSwitchLogicalState; node["switchNames"] >> rhs.functionSwitchNames; + // Custom USB joytsick mapping + node["usbJoystickExtMode"] >> rhs.usbJoystickExtMode; + node["usbJoystickIfMode"] >> usbJoystickIfModeLut >> rhs.usbJoystickIfMode; + node["usbJoystickCircularCut"] >> rhs.usbJoystickCircularCut; + node["usbJoystickCh"] >> rhs.usbJoystickCh; + // preferably perform conversions here to avoid cluttering the field decodes if (SemanticVersion(QString(rhs.semver)) < SemanticVersion("2.8.0")) { diff --git a/companion/src/firmwares/edgetx/yaml_usbjoystickdata.cpp b/companion/src/firmwares/edgetx/yaml_usbjoystickdata.cpp new file mode 100644 index 000000000..ca6581eb3 --- /dev/null +++ b/companion/src/firmwares/edgetx/yaml_usbjoystickdata.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 for more details. + */ + +#include "yaml_usbjoystickdata.h" +#include "generalsettings.h" +#include "modeldata.h" + +const YamlLookupTable usbJoystickChMode = { + {0, "CH_NONE"}, + {1, "CH_BUTTON"}, + {2, "CH_AXIS"}, + {3, "CH_SIM"}, +}; + +namespace YAML +{ + +Node convert::encode(const USBJoystickChData& rhs) +{ + Node node; + node["mode"] = usbJoystickChMode << rhs.mode; + node["inversion"] = rhs.inversion; + node["param"] = rhs.param; + node["btn_num"] = rhs.btn_num; + node["switch_npos"] = rhs.switch_npos; + return node; +} + +bool convert::decode(const Node& node, USBJoystickChData& rhs) +{ + node["mode"] >> usbJoystickChMode >> rhs.mode; + node["inversion"] >> rhs.inversion; + node["param"] >> rhs.param; + node["btn_num"] >> rhs.btn_num; + node["switch_npos"] >> rhs.switch_npos; + return true; +} + +} // namespace YAML diff --git a/companion/src/firmwares/edgetx/yaml_usbjoystickdata.h b/companion/src/firmwares/edgetx/yaml_usbjoystickdata.h new file mode 100644 index 000000000..d3967bfd9 --- /dev/null +++ b/companion/src/firmwares/edgetx/yaml_usbjoystickdata.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 for more details. + */ + +#include "yaml_ops.h" +#include "generalsettings.h" + +extern const YamlLookupTable usbJoystickChMode; + +class USBJoystickChData; + +namespace YAML +{ +template <> +struct convert { + static Node encode(const USBJoystickChData& rhs); + static bool decode(const Node& node, USBJoystickChData& rhs); +}; +} // namespace YAML diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h index 7e8971809..4f988f08c 100644 --- a/companion/src/firmwares/modeldata.h +++ b/companion/src/firmwares/modeldata.h @@ -90,6 +90,19 @@ enum TrainerMode { #define INPUT_NAME_LEN 4 #define CPN_MAX_BITMAP_LEN 14 +#define CPN_USBJ_MAX_JOYSTICK_CHANNELS 26 + +class USBJoystickChData { + public: + USBJoystickChData() { clear(); } + unsigned int mode; + unsigned int inversion; + unsigned int param; + unsigned int btn_num; + unsigned int switch_npos; + void clear() { memset(reinterpret_cast(this), 0, sizeof(USBJoystickChData)); } +}; + class ModelData { Q_DECLARE_TR_FUNCTIONS(ModelData) @@ -201,6 +214,12 @@ class ModelData { unsigned int functionSwitchLogicalState; char functionSwitchNames[CPN_MAX_FUNCTION_SWITCHES][HARDWARE_NAME_LEN + 1]; + // Custom USB joytsick mapping + unsigned int usbJoystickExtMode; + unsigned int usbJoystickIfMode; + unsigned int usbJoystickCircularCut; + USBJoystickChData usbJoystickCh[CPN_USBJ_MAX_JOYSTICK_CHANNELS]; + void clear(); bool isEmpty() const; void setDefaultInputs(const GeneralSettings & settings); diff --git a/companion/src/modeledit/node.cpp b/companion/src/modeledit/node.cpp index 302bfebc8..640dce172 100644 --- a/companion/src/modeledit/node.cpp +++ b/companion/src/modeledit/node.cpp @@ -116,7 +116,7 @@ qreal Node::getY() if(scene()) { QRectF rect = scene()->sceneRect(); - return 1+((y()-rect.bottom())*2/rect.height()); + return ((rect.bottom()-y())*2/rect.height())-1; } return 0; } @@ -126,7 +126,7 @@ void Node::setY(qreal newY) if(scene()) { QRectF rect = scene()->sceneRect(); - QGraphicsObject::setY(rect.top() + (newY+1) * rect.height()/2); + QGraphicsObject::setY(rect.top() + (1-newY) * rect.height()/2); } } diff --git a/companion/src/simulation/joystick.cpp b/companion/src/simulation/joystick.cpp index d34f72e6a..34f56726b 100644 --- a/companion/src/simulation/joystick.cpp +++ b/companion/src/simulation/joystick.cpp @@ -63,6 +63,9 @@ bool Joystick::open(int stick) numHats = SDL_JoystickNumHats(joystick); numTrackballs = SDL_JoystickNumBalls(joystick); joystickTimer.start(eventTimeout); + for (int i = 0; i < numButtons; i += 1) { + buttons[i] = -1; + } return true; } else { @@ -109,9 +112,9 @@ void Joystick::processEvents() axisRepeatTimers[i].restart(); } } - else { - emit axisValueChanged(i, 0); - } +// else { +// emit axisValueChanged(i, 0); +// } } for (i = 0; i < numButtons; i++) { Uint8 changed = SDL_JoystickGetButton(joystick, i); diff --git a/companion/src/simulation/joystickdialog.cpp b/companion/src/simulation/joystickdialog.cpp index 1be3dd508..a43e5b501 100644 --- a/companion/src/simulation/joystickdialog.cpp +++ b/companion/src/simulation/joystickdialog.cpp @@ -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(QRegularExpression("jsmapCB_[0-9]+"))) { - populateSourceCombo(cb); - sticks[cb->property("channel").toUInt()] = cb; - } - foreach(QCheckBox *ib, findChildren(QRegularExpression("ChInv_[0-9]+"))) { - invert[ib->property("channel").toUInt()] = ib; - } - foreach(QSlider *sl, findChildren(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("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; jsensitivities[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(); } diff --git a/companion/src/simulation/joystickdialog.h b/companion/src/simulation/joystickdialog.h index cdb85593a..4e3b50e18 100644 --- a/companion/src/simulation/joystickdialog.h +++ b/companion/src/simulation/joystickdialog.h @@ -29,6 +29,14 @@ class QCheckBox; class QComboBox; class QSlider; +enum JSButtonFlag { + JS_BUTTON_TOGGLE = 0x1000, + JS_BUTTON_3POS_UP = 0x2000, + JS_BUTTON_3POS_DN = 0x4000, + JS_BUTTON_TYPE_MASK = 0xF000, + JS_BUTTON_SWITCH_MASK = 0x1F +}; + namespace Ui { class joystickDialog; } @@ -44,20 +52,22 @@ class joystickDialog : public QDialog private: Ui::joystickDialog *ui; - int jscal[MAX_JOYSTICKS][3]; - QCheckBox * invert[MAX_JOYSTICKS]; - QComboBox * sticks[MAX_JOYSTICKS]; - QSlider * sliders[MAX_JOYSTICKS]; + int jscal[MAX_JS_AXES][3]; + QCheckBox * invert[MAX_JS_AXES]; + QComboBox * sticks[MAX_JS_AXES + MAX_JS_BUTTONS]; + QSlider * sliders[MAX_JS_AXES + MAX_JS_BUTTONS]; int step; int numAxes; bool started; private slots: void populateSourceCombo(QComboBox * cb); + void populateButtonCombo(QComboBox * cb); bool loadJoysticks(int stick = -1); void joystickOpen(int stick); void joystickSetEnabled(bool enable); void onjoystickAxisValueChanged(int axis, int value); + void onjoystickButtonValueChanged(int button, bool state); void loadStep(); void on_backButton_clicked(); void on_nextButton_clicked(); diff --git a/companion/src/simulation/joystickdialog.ui b/companion/src/simulation/joystickdialog.ui index eb2be2965..1cb2b25d1 100644 --- a/companion/src/simulation/joystickdialog.ui +++ b/companion/src/simulation/joystickdialog.ui @@ -6,8 +6,8 @@ 0 0 - 554 - 351 + 709 + 195 @@ -20,495 +20,23 @@ Configure Joystick - - - - Qt::Horizontal - - - - 0 - 20 - - - - false + + + 640 + 0 + + 6 - - - - - Ch2 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 2 - - - - - - - - 0 - 0 - - - - 1 - - - - - - - Ch1 - - - - - - - - 0 - 0 - - - - 2 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 1 - - - - - - - - 0 - 0 - - - - 0 - - - - - - - Ch4 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 4 - - - - - - - Ch6 - - - - - - - Ch3 - - - - - - - - 0 - 0 - - - - 4 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 3 - - - - - - - - 0 - 0 - - - - 3 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 5 - - - - - - - - 0 - 0 - - - - 5 - - - - - - - Ch5 - - - - - - - Ch7 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 6 - - - - - - - - 0 - 0 - - - - 6 - - - - - - - Ch8 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 7 - - - - - - - - 0 - 0 - - - - 7 - - - - - - - -32767 - - - 32676 - - - Qt::Horizontal - - - false - - - false - - - QSlider::NoTicks - - - 0 - - - 0 - - - - - - - - - - 0 - - - - - - - - - - 1 - - - - - - - - - - 2 - - - - - - - - - - 3 - - - - - - - - - - 4 - - - - - - - - - - 5 - - - - - - - - - - 6 - - - - - - - - - - 7 - - - - + @@ -544,6 +72,19 @@ + + + + Qt::Horizontal + + + + 0 + 1 + + + + @@ -587,7 +128,7 @@ - 198 + 360 20 @@ -647,7 +188,7 @@ - 20 + 1 0 @@ -661,7 +202,7 @@ 0 - 20 + 1 diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index 6ff5978b1..92933f17a 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -21,7 +21,6 @@ #include "simulatorwidget.h" #include "ui_simulatorwidget.h" -#include "appdata.h" #include "appdebugmessagehandler.h" #include "radiofaderwidget.h" #include "radiokeywidget.h" @@ -619,7 +618,6 @@ void SimulatorWidget::setupRadioWidgets() RadioKnobWidget * pot = new RadioKnobWidget(Board::PotType(radioSettings.potConfig[i]), wname, 0, ui->radioWidgetsHT); pot->setIndex(i); ui->radioWidgetsHTLayout->insertWidget(midpos++, pot); - m_radioWidgets.append(pot); } @@ -661,6 +659,7 @@ void SimulatorWidget::setupRadioWidgets() foreach (RadioWidget * rw, m_radioWidgets) { connect(rw, &RadioWidget::valueChange, this, &SimulatorWidget::onRadioWidgetValueChange); connect(this, &SimulatorWidget::widgetValueChange, rw, &RadioWidget::setValueQual); + connect(this, &SimulatorWidget::widgetValueAdjust, rw, &RadioWidget::chgValueQual); connect(this, &SimulatorWidget::widgetStateChange, rw, &RadioWidget::setStateData); } @@ -673,21 +672,28 @@ void SimulatorWidget::setupJoysticks() if (g.jsSupport() && g.jsCtrl() > -1) { if (!joystick) - joystick = new Joystick(this); + joystick = new Joystick(this, SDL_JOYSTICK_DEFAULT_EVENT_TIMEOUT, false, SDL_JOYSTICK_DEFAULT_AUTOREPEAT_DELAY); else joystick->close(); if (joystick && joystick->open(g.jsCtrl())) { - int numAxes = std::min(joystick->numAxes, MAX_JOYSTICKS); + int numAxes = std::min(joystick->numAxes, MAX_JS_AXES); for (int j=0; jsensitivities[j] = 0; joystick->deadzones[j] = 0; } connect(joystick, &Joystick::axisValueChanged, this, &SimulatorWidget::onjoystickAxisValueChanged); - if (vJoyLeft) + connect(joystick, &Joystick::buttonValueChanged, this, &SimulatorWidget::onjoystickButtonValueChanged); + if (vJoyLeft) { connect(this, &SimulatorWidget::stickValueChange, vJoyLeft, &VirtualJoystickWidget::setStickAxisValue); - if (vJoyRight) + connect(this, &SimulatorWidget::widgetValueAdjust, vJoyLeft->horizontalTrim(), &RadioWidget::chgValueQual); + connect(this, &SimulatorWidget::widgetValueAdjust, vJoyLeft->verticalTrim(), &RadioWidget::chgValueQual); + } + if (vJoyRight) { connect(this, &SimulatorWidget::stickValueChange, vJoyRight, &VirtualJoystickWidget::setStickAxisValue); + connect(this, &SimulatorWidget::widgetValueAdjust, vJoyRight->horizontalTrim(), &RadioWidget::chgValueQual); + connect(this, &SimulatorWidget::widgetValueAdjust, vJoyRight->verticalTrim(), &RadioWidget::chgValueQual); + } joysticksEnabled = true; } else { @@ -858,7 +864,7 @@ void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value) static const int ttlFaders = Boards::getCapability(m_board, Board::Sliders); static const int valueRange = 1024; - if (!joystick || axis >= MAX_JOYSTICKS) + if (!joystick || axis >= MAX_JS_AXES) return; int dlta; @@ -885,11 +891,58 @@ void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value) } else { stick -= ttlSticks; - if (stick < ttlKnobs) + if (stick < ttlKnobs) { + GeneralSettings radioSettings = GeneralSettings(); + if (Board::PotType(radioSettings.potConfig[stick]) == Board::POT_MULTIPOS_SWITCH) + stickval += 1024; emit widgetValueChange(RadioWidget::RADIO_WIDGET_KNOB, stick, stickval); - else - emit widgetValueChange(RadioWidget::RADIO_WIDGET_FADER, stick - ttlKnobs, stickval); + } else { + stick -= ttlKnobs; + emit widgetValueChange(RadioWidget::RADIO_WIDGET_FADER, stick, stickval); + } } #endif } + +void SimulatorWidget::onjoystickButtonValueChanged(int button, bool state) +{ +#ifdef JOYSTICKS + + if (!joystick || button >= MAX_JS_BUTTONS) + return; + + int ttlSwitches = Boards::getCapability(m_board, Board::Switches); + + int btn = g.jsButton[button].button_idx(); + + if (g.jsButton[button].button_inv()) + state = !state; + + int swtch = btn & JS_BUTTON_SWITCH_MASK; + + if (swtch < ttlSwitches) { + if (btn & JS_BUTTON_3POS_DN) { + // 3POS Down + if (state || (switchDirection[swtch] == 0) || (switchDirection[swtch] == (btn & JS_BUTTON_TYPE_MASK))) { + emit widgetValueChange(RadioWidget::RADIO_WIDGET_SWITCH, swtch, state ? 1 : 0); + switchDirection[swtch] = (btn & JS_BUTTON_TYPE_MASK); + } + } else if (btn & JS_BUTTON_3POS_UP) { + // 3POS Up + if (state || (switchDirection[swtch] == 0) || (switchDirection[swtch] == (btn & JS_BUTTON_TYPE_MASK))) { + emit widgetValueChange(RadioWidget::RADIO_WIDGET_SWITCH, swtch, state ? -1 : 0); + switchDirection[swtch] = (btn & JS_BUTTON_TYPE_MASK); + } + } else if (btn & JS_BUTTON_TOGGLE) { + // Toggle or momentary + emit widgetValueChange(RadioWidget::RADIO_WIDGET_SWITCH, swtch, state ? 1 : - 1); + } + } else { + // Trim + swtch -= ttlSwitches; + int offset = (btn & JS_BUTTON_3POS_DN) ? -1 : 1; + emit widgetValueAdjust(RadioWidget::RADIO_WIDGET_TRIM, swtch, offset, state); + } +#endif +} diff --git a/companion/src/simulation/simulatorwidget.h b/companion/src/simulation/simulatorwidget.h index 62ee4c8ef..08ec1d7c3 100644 --- a/companion/src/simulation/simulatorwidget.h +++ b/companion/src/simulation/simulatorwidget.h @@ -21,6 +21,7 @@ #ifndef _SIMULATORWIDGET_H_ #define _SIMULATORWIDGET_H_ +#include "appdata.h" #include "constants.h" #include "helpers.h" #include "radiodata.h" @@ -88,6 +89,7 @@ class SimulatorWidget : public QWidget void stickValueChange(int axis, int value); void stickModeChange(const unsigned mode); void widgetValueChange(const RadioWidget::RadioWidgetType type, const int index, const int value); + void widgetValueAdjust(const RadioWidget::RadioWidgetType type, const int index, const int offset, const bool state); void widgetStateChange(const RadioWidget::RadioWidgetState & state); void inputValueChange(int type, quint8 index, qint16 value); void simulatorSetData(const QByteArray & data); @@ -110,6 +112,7 @@ class SimulatorWidget : public QWidget void onSimulatorError(const QString & error); void onRadioWidgetValueChange(const RadioWidget::RadioWidgetType type, const int index, int value); void onjoystickAxisValueChanged(int axis, int value); + void onjoystickButtonValueChanged(int button, bool state); void setRadioProfileId(int value); void setupRadioWidgets(); @@ -145,6 +148,7 @@ class SimulatorWidget : public QWidget #ifdef JOYSTICKS Joystick * joystick = nullptr; + int switchDirection[MAX_JS_BUTTONS]; #endif }; diff --git a/companion/src/simulation/widgets/radiotrimwidget.h b/companion/src/simulation/widgets/radiotrimwidget.h index 54659b0a4..dc80f7182 100644 --- a/companion/src/simulation/widgets/radiotrimwidget.h +++ b/companion/src/simulation/widgets/radiotrimwidget.h @@ -65,7 +65,7 @@ class RadioTrimWidget : public RadioWidget setValue(value); } - void setValue(const int & value) + void setValue(const int & value) override { if (sender() && qobject_cast(sender())) { RadioWidget::setValue(value); @@ -75,6 +75,12 @@ class RadioTrimWidget : public RadioWidget } } + void chgValueQual(const RadioWidgetType & type, const int & index, const int offset, const bool state) override { + if (type == m_type && index == m_index) { + emit valueChange(m_type, (offset < 0) ? m_btnDecIndex : m_btnIncIndex, state ? RADIO_TRIM_BTN_ON : RADIO_TRIM_BTN_OFF); + } + } + protected slots: void init(Qt::Orientation orientation) diff --git a/companion/src/simulation/widgets/radiowidget.h b/companion/src/simulation/widgets/radiowidget.h index af218495b..9742e2e15 100644 --- a/companion/src/simulation/widgets/radiowidget.h +++ b/companion/src/simulation/widgets/radiowidget.h @@ -88,6 +88,7 @@ class RadioWidget : public QWidget virtual void setType(const RadioWidgetType & type); virtual void setValue(const int & value); virtual void setValueQual(const RadioWidgetType & type, const int & index, const int & value); + virtual void chgValueQual(const RadioWidgetType & type, const int & index, const int offset, const bool state) {} virtual void setFlags(const quint16 & flags); virtual void setShowLabel(const bool show); virtual void setLabelText(const QString & labelText, bool showLabel = true); diff --git a/companion/src/simulation/widgets/virtualjoystickwidget.cpp b/companion/src/simulation/widgets/virtualjoystickwidget.cpp index 4da39137e..e063f6cfd 100644 --- a/companion/src/simulation/widgets/virtualjoystickwidget.cpp +++ b/companion/src/simulation/widgets/virtualjoystickwidget.cpp @@ -323,7 +323,7 @@ void VirtualJoystickWidget::onNodeXChanged() void VirtualJoystickWidget::onNodeYChanged() { - emit valueChange(RadioWidget::RADIO_WIDGET_STICK, m_yIndex, int(-m_stickScale * getStickY())); + emit valueChange(RadioWidget::RADIO_WIDGET_STICK, m_yIndex, int(m_stickScale * getStickY())); updateNodeValueLabels(); } @@ -564,9 +564,9 @@ void VirtualJoystickWidget::onButtonChange(bool checked) void VirtualJoystickWidget::updateNodeValueLabels() { if (nodeLabelX) - nodeLabelX->setText(QString("%1").arg(node->getX() * 100.0f, 2, 'f', 0)); + nodeLabelX->setText(QString("%1").arg(getStickX() * 100.0f, 2, 'f', 0)); if (nodeLabelY) - nodeLabelY->setText(QString("%1").arg(node->getY() * -100.0f, 2, 'f', 0)); + nodeLabelY->setText(QString("%1").arg(getStickY() * 100.0f, 2, 'f', 0)); } void VirtualJoystickWidget::onGsMouseEvent(QGraphicsSceneMouseEvent * event) diff --git a/companion/src/simulation/widgets/virtualjoystickwidget.h b/companion/src/simulation/widgets/virtualjoystickwidget.h index 5507d46e2..ac336f8e5 100644 --- a/companion/src/simulation/widgets/virtualjoystickwidget.h +++ b/companion/src/simulation/widgets/virtualjoystickwidget.h @@ -61,6 +61,9 @@ class VirtualJoystickWidget : public QWidget int getStickScale() const; void setStickScale(int stickScale); + const RadioTrimWidget *horizontalTrim() { return hTrimWidget; } + const RadioTrimWidget *verticalTrim() { return vTrimWidget; } + public slots: void setStickX(qreal x); void setStickY(qreal y); diff --git a/companion/src/storage/appdata.cpp b/companion/src/storage/appdata.cpp index e98c2c972..066469fed 100644 --- a/companion/src/storage/appdata.cpp +++ b/companion/src/storage/appdata.cpp @@ -256,6 +256,16 @@ bool JStickData::existsOnDisk() return (m_settings.value(settingsPath() % stick_axe_key(), -1).toInt() > -1); } +JButtonData::JButtonData() : CompStoreObj(), index(-1) +{ + CompStoreObj::addObjectMapping(propertyGroup(), this); +} + +bool JButtonData::existsOnDisk() +{ + return (m_settings.value(settingsPath() % button_idx_key(), -1).toInt() > -1); +} + // ** Profile class******************** @@ -397,8 +407,10 @@ AppData::AppData() : profile[i].setIndex(i); // Configure the joysticks - for (int i = 0; i < MAX_JOYSTICKS; i++) + for (int i = 0; i < MAX_JS_AXES; i++) joystick[i].setIndex(i); + for (int i = 0; i < MAX_JS_BUTTONS; i++) + jsButton[i].setIndex(i); // Configure the updates for (int i = 0; i < MAX_COMPONENTS; i++) { @@ -436,8 +448,10 @@ void AppData::initAll() for (int i = 0; i < MAX_PROFILES; i++) profile[i].init(); // Initialize the joysticks - for (int i = 0; i < MAX_JOYSTICKS; i++) + for (int i = 0; i < MAX_JS_AXES; i++) joystick[i].init(); + for (int i = 0; i < MAX_JS_BUTTONS; i++) + jsButton[i].init(); // Initialize the updatess for (int i = 0; i < MAX_COMPONENTS; i++) { component[i].init(); @@ -453,8 +467,10 @@ void AppData::resetAllSettings() fwRev.resetAll(); for (int i = 0; i < MAX_PROFILES; i++) profile[i].resetAll(); - for (int i = 0; i < MAX_JOYSTICKS; i++) + for (int i = 0; i < MAX_JS_AXES; i++) joystick[i].resetAll(); + for (int i = 0; i < MAX_JS_BUTTONS; i++) + jsButton[i].resetAll(); for (int i = 0; i < MAX_COMPONENTS; i++) { component[i].resetAll(); for (int j = 0; j < MAX_COMPONENT_ASSETS; j++) { @@ -469,8 +485,10 @@ void AppData::storeAllSettings() storeAll(); for (int i = 0; i < MAX_PROFILES; i++) profile[i].storeAll(); - for (int i = 0; i < MAX_JOYSTICKS; i++) + for (int i = 0; i < MAX_JS_AXES; i++) joystick[i].storeAll(); + for (int i = 0; i < MAX_JS_BUTTONS; i++) + jsButton[i].storeAll(); for (int i = 0; i < MAX_COMPONENTS; i++) { component[i].storeAll(); for (int j = 0; j < MAX_COMPONENT_ASSETS; j++) diff --git a/companion/src/storage/appdata.h b/companion/src/storage/appdata.h index b0721a3fe..88e9a3da6 100644 --- a/companion/src/storage/appdata.h +++ b/companion/src/storage/appdata.h @@ -59,7 +59,8 @@ #define CPN_SETTINGS_INI_PATH QString(CPN_SETTINGS_BACKUP_DIR % "/" % CPN_SETTINGS_INI_FILE) #define MAX_PROFILES 20 -#define MAX_JOYSTICKS 8 +#define MAX_JS_AXES 10 +#define MAX_JS_BUTTONS 32 #define MAX_COMPONENTS 10 #define MAX_COMPONENT_ASSETS 5 @@ -347,6 +348,26 @@ class JStickData: public CompStoreObj int index; }; +class JButtonData: public CompStoreObj +{ + Q_OBJECT + public slots: + bool existsOnDisk(); + + protected: + explicit JButtonData(); + void setIndex(int idx) { index = idx; } + inline QString propertyGroup() const override { return QStringLiteral("JsButton"); } + inline QString settingsPath() const override { return QString("%1/%2/").arg(propertyGroup()).arg(index); } + friend class AppData; + + private: + PROPERTY(int, button_idx, -1) + PROPERTY(int, button_inv, 0) + + int index; +}; + //! \brief Profile class stores properties related to each Radio Profile. //! \todo TODO: Remove or refactor stored radio settings system (#4583) @@ -592,7 +613,8 @@ class AppData: public CompStoreObj bool exportSettingsToFile(const QString & expFile, QString & resultMsg); Profile profile[MAX_PROFILES]; - JStickData joystick[MAX_JOYSTICKS]; + JStickData joystick[MAX_JS_AXES]; + JButtonData jsButton[MAX_JS_BUTTONS]; FwRevision fwRev; ComponentData component[MAX_COMPONENTS]; diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index fabdc46a0..c61117021 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -61,6 +61,12 @@ option(HARDWARE_TRAINER_MULTI "Allow multi trainer" OFF) option(BOOTLOADER "Include Bootloader" ON) option(FWDRIVE "Attach also firmware drive with USB" OFF) +if(PCB STREQUAL X9D+ AND PCBREV STREQUAL 2019) + option(USBJ_EX "Enable USB Joystick Extension" OFF) +else() + option(USBJ_EX "Enable USB Joystick Extension" ON) +endif() + # since we reset all default CMAKE compiler flags for firmware builds, provide an alternate way for user to specify additional flags. set(FIRMWARE_C_FLAGS "" CACHE STRING "Additional flags for firmware target c compiler (note: all CMAKE_C_FLAGS[_*] are ignored for firmware/bootloader).") set(FIRMWARE_C_FLAGS_DEBUG "-g" CACHE STRING "Additional flags for firmware target (Debug config) c compiler (note: CMAKE_C_FLAGS_DEBUG is ignored for firmware/bootloader).") @@ -418,6 +424,14 @@ if(GUI) endforeach() endif() + +if(USBJ_EX) + add_definitions(-DUSBJ_EX) + set(SRC + ${SRC} + usb_joystick.cpp + ) +endif() foreach(FILE ${TARGET_SRC}) set(SRC targets/${TARGET_DIR}/${FILE} ${SRC}) endforeach() diff --git a/radio/src/bitmaps/480x272/default_theme/mask_model_usb.png b/radio/src/bitmaps/480x272/default_theme/mask_model_usb.png new file mode 100644 index 000000000..c7c0cd9ce Binary files /dev/null and b/radio/src/bitmaps/480x272/default_theme/mask_model_usb.png differ diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 70ff8af95..cd773470a 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -118,36 +118,36 @@ static inline void check_struct() #if defined(PCBXLITES) CHKSIZE(RadioData, 862); - CHKSIZE(ModelData, 6164); + CHKSIZE(ModelData, 6217); #elif defined(PCBXLITE) CHKSIZE(RadioData, 860); - CHKSIZE(ModelData, 6164); + CHKSIZE(ModelData, 6217); #elif defined(RADIO_TPRO) CHKSIZE(RadioData, 843); - CHKSIZE(ModelData, 6189); + CHKSIZE(ModelData, 6242); #elif defined(RADIO_BOXER) CHKSIZE(RadioData, 875); - CHKSIZE(ModelData, 6165); + CHKSIZE(ModelData, 6218); #elif defined(PCBX7) CHKSIZE(RadioData, 866); - CHKSIZE(ModelData, 6164); + CHKSIZE(ModelData, 6217); #elif defined(PCBX9E) CHKSIZE(RadioData, 956); - CHKSIZE(ModelData, 6616); + CHKSIZE(ModelData, 6669); #elif defined(PCBX9D) || defined(PCBX9DP) CHKSIZE(RadioData, 898); - CHKSIZE(ModelData, 6608); + CHKSIZE(ModelData, 6661); #elif defined(PCBHORUS) #if defined(PCBX10) CHKSIZE(RadioData, 924); - CHKSIZE(ModelData, 15378); + CHKSIZE(ModelData, 15431); #else CHKSIZE(RadioData, 906); - CHKSIZE(ModelData, 15376); + CHKSIZE(ModelData, 15429); #endif #elif defined(PCBNV14) CHKSIZE(RadioData, 852); - CHKSIZE(ModelData, 15192); + CHKSIZE(ModelData, 15245); #endif #undef CHKSIZE diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index 0c42b241f..f6cd6a276 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -29,6 +29,7 @@ #include "opentx_types.h" #include "globals.h" #include "serial.h" +#include "usb_joystick.h" #if defined(PCBTARANIS) #define N_TARANIS_FIELD(x) @@ -624,6 +625,36 @@ PACK(struct PartialModel { TimerData timers[MAX_TIMERS]; }); +/* + * USB Joystick channel structure + */ + +PACK(struct USBJoystickChData { + uint8_t mode:3 ENUM(USBJoystickCh); + uint8_t inversion:1; + uint8_t param:4; + uint8_t btn_num:5; + uint8_t switch_npos:3; + +#if defined(USBJ_EX) + NOBACKUP( + uint8_t lastBtnNumNoCLip() { + uint8_t last = btn_num + switch_npos; + // Use one less joystick button for 2POS and 3POS switches for Companion mode + if ((param == USBJOYS_BTN_MODE_COMPANION) && (switch_npos > 0) && (switch_npos < 3)) last -= 1; + return last; + } + uint8_t lastBtnNum() { + uint8_t last = lastBtnNumNoCLip(); + if (last >= USBJ_BUTTON_SIZE) { + last = USBJ_BUTTON_SIZE - 1; + } + return last; + } + ); +#endif +}); + PACK(struct ModelData { CUST_ATTR(semver,nullptr,w_semver); ModelHeader header; @@ -725,6 +756,11 @@ PACK(struct ModelData { break; } } + + uint8_t usbJoystickExtMode:1; + uint8_t usbJoystickIfMode:3 ENUM(USBJoystickIfMode); + uint8_t usbJoystickCircularCut:4; + USBJoystickChData usbJoystickCh[USBJ_MAX_JOYSTICK_CHANNELS]; }); /* diff --git a/radio/src/gui/128x64/menus.h b/radio/src/gui/128x64/menus.h index 12281929c..79ea7b869 100644 --- a/radio/src/gui/128x64/menus.h +++ b/radio/src/gui/128x64/menus.h @@ -131,6 +131,9 @@ void menuModelSensor(event_t event); void menuModelDisplay(event_t event); void menuModelTemplates(event_t event); void menuModelGVarOne(event_t event); +#if defined(USBJ_EX) +void menuModelUSBJoystick(event_t event); +#endif static const MenuHandlerFunc menuTabModel[] = { menuModelSelect, diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 0dec41adc..42ce9cc39 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -22,6 +22,10 @@ #include "opentx.h" #include "mixer_scheduler.h" +#if defined(USBJ_EX) +#include "usb_joystick.h" +#endif + #if !defined(EEPROM) #include "storage/sdcard_common.h" #include "storage/modelslist.h" @@ -193,6 +197,15 @@ enum MenuModelSetupItems { ITEM_MODEL_SETUP_TRAINER_CHANNELS, ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS, #endif + +#if defined(USBJ_EX) + ITEM_MODEL_SETUP_USBJOYSTICK_LABEL, + ITEM_MODEL_SETUP_USBJOYSTICK_MODE, + ITEM_MODEL_SETUP_USBJOYSTICK_IF_MODE, + ITEM_MODEL_SETUP_USBJOYSTICK_CIRC_CUTOUT, + ITEM_MODEL_SETUP_USBJOYSTICK_CH_BUTTON, + ITEM_MODEL_SETUP_USBJOYSTICK_APPLY, +#endif ITEM_MODEL_SETUP_LINES_COUNT }; @@ -443,6 +456,25 @@ void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event) NUM_SWITCHES - 1, /* Switch warning */ #endif +#if defined(USBJ_EX) +inline uint8_t USB_JOYSTICK_EXTROW() +{ + return (usbJoystickExtMode() ? (uint8_t)0 : HIDDEN_ROW); +} + +inline uint8_t USB_JOYSTICK_APPLYROW() +{ + if(!usbJoystickExtMode()) return HIDDEN_ROW; + if(usbJoystickSettingsChanged()) return (uint8_t)0; + return READONLY_ROW; +} + +#define USB_JOYSTICK_ROWS LABEL(USBJoystick), (uint8_t)0, USB_JOYSTICK_EXTROW(), \ + USB_JOYSTICK_EXTROW(), USB_JOYSTICK_EXTROW(), USB_JOYSTICK_APPLYROW() +#else +#define USB_JOYSTICK_ROWS +#endif + #if defined(FUNCTION_SWITCHES) static const char* _fct_sw_start[] = { STR_CHAR_UP, STR_CHAR_DOWN, "=" }; #endif @@ -491,7 +523,9 @@ void menuModelSetup(event_t event) EXTRA_MODULE_ROWS - TRAINER_ROWS + TRAINER_ROWS, + + USB_JOYSTICK_ROWS }); MENU_CHECK(menuTabModel, MENU_MODEL_SETUP, HEADER_LINE + ITEM_MODEL_SETUP_LINES_COUNT); @@ -2053,6 +2087,39 @@ void menuModelSetup(event_t event) } break; #endif + +#if defined(USBJ_EX) + case ITEM_MODEL_SETUP_USBJOYSTICK_LABEL: + lcdDrawTextAlignedLeft(y, STR_USBJOYSTICK_LABEL); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_MODE: + g_model.usbJoystickExtMode = editChoice(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_USBJOYSTICK_EXTMODE, STR_VUSBJOYSTICK_EXTMODE, g_model.usbJoystickExtMode, 0, 1, attr, event); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_IF_MODE: + g_model.usbJoystickIfMode = editChoice(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_USBJOYSTICK_IF_MODE, STR_VUSBJOYSTICK_IF_MODE, g_model.usbJoystickIfMode, 0, USBJOYS_LAST, attr, event); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_CIRC_CUTOUT: + g_model.usbJoystickCircularCut = editChoice(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_USBJOYSTICK_CIRC_COUTOUT, STR_VUSBJOYSTICK_CIRC_COUTOUT, g_model.usbJoystickCircularCut, 0, 2, attr, event); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_CH_BUTTON: + lcdDrawText(INDENT_WIDTH, y, BUTTON(TR_USBJOYSTICK_SETTINGS), attr); + if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { + pushMenu(menuModelUSBJoystick); + } + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_APPLY: + lcdDrawText(INDENT_WIDTH, y, BUTTON(TR_USBJOYSTICK_APPLY_CHANGES), attr); + if (attr && !READ_ONLY() && event == EVT_KEY_FIRST(KEY_ENTER)) { + killEvents(event); + onUSBJoystickModelChanged(); + } + break; +#endif } } diff --git a/radio/src/gui/212x64/menus.h b/radio/src/gui/212x64/menus.h index b88ed4dd4..bfcb2d562 100644 --- a/radio/src/gui/212x64/menus.h +++ b/radio/src/gui/212x64/menus.h @@ -122,6 +122,9 @@ void menuModelCustomScripts(event_t event); void menuModelTelemetry(event_t event); void menuModelSensor(event_t event); void menuModelDisplay(event_t event); +#if defined(USBJ_EX) +void menuModelUSBJoystick(event_t event); +#endif extern const MenuHandlerFunc menuTabModel[MENU_MODEL_PAGES_COUNT]; diff --git a/radio/src/gui/212x64/model_setup.cpp b/radio/src/gui/212x64/model_setup.cpp index 9757dbc90..84a0ea986 100644 --- a/radio/src/gui/212x64/model_setup.cpp +++ b/radio/src/gui/212x64/model_setup.cpp @@ -22,6 +22,10 @@ #include "opentx.h" #include "mixer_scheduler.h" +#if defined(USBJ_EX) +#include "usb_joystick.h" +#endif + #if defined(CROSSFIRE) #include "telemetry/crossfire.h" #endif @@ -153,6 +157,15 @@ enum MenuModelSetupItems { #endif ITEM_MODEL_SETUP_TRAINER_CHANNELS, ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS, + +#if defined(USBJ_EX) + ITEM_MODEL_SETUP_USBJOYSTICK_LABEL, + ITEM_MODEL_SETUP_USBJOYSTICK_MODE, + ITEM_MODEL_SETUP_USBJOYSTICK_IF_MODE, + ITEM_MODEL_SETUP_USBJOYSTICK_CIRC_CUTOUT, + ITEM_MODEL_SETUP_USBJOYSTICK_CH_BUTTON, + ITEM_MODEL_SETUP_USBJOYSTICK_APPLY, +#endif ITEM_MODEL_SETUP_LINES_COUNT }; @@ -392,6 +405,25 @@ void onBluetoothConnectMenu(const char * result) #include "common/stdlcd/model_setup_afhds3.cpp" #endif +#if defined(USBJ_EX) +inline uint8_t USB_JOYSTICK_EXTROW() +{ + return (usbJoystickExtMode() ? (uint8_t)0 : HIDDEN_ROW); +} + +inline uint8_t USB_JOYSTICK_APPLYROW() +{ + if(!usbJoystickExtMode()) return HIDDEN_ROW; + if(usbJoystickSettingsChanged()) return (uint8_t)0; + return READONLY_ROW; +} + +#define USB_JOYSTICK_ROWS LABEL(USBJoystick), (uint8_t)0, USB_JOYSTICK_EXTROW(), \ + USB_JOYSTICK_EXTROW(), USB_JOYSTICK_EXTROW(), USB_JOYSTICK_APPLYROW() +#else +#define USB_JOYSTICK_ROWS +#endif + void menuModelSetup(event_t event) { horzpos_t l_posHorz = menuHorizontalPosition; @@ -470,7 +502,9 @@ void menuModelSetup(event_t event) IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3 - TRAINER_ROWS + TRAINER_ROWS, + + USB_JOYSTICK_ROWS }); MENU_CHECK(menuTabModel, MENU_MODEL_SETUP, ITEM_MODEL_SETUP_LINES_COUNT); @@ -1768,6 +1802,39 @@ void menuModelSetup(event_t event) modelSetupModulePxx2ReceiverLine(CURRENT_MODULE_EDITED(k), CURRENT_RECEIVER_EDITED(k), y, event, attr); break; #endif + +#if defined(USBJ_EX) + case ITEM_MODEL_SETUP_USBJOYSTICK_LABEL: + lcdDrawTextAlignedLeft(y, STR_USBJOYSTICK_LABEL); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_MODE: + g_model.usbJoystickExtMode = editChoice(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_USBJOYSTICK_EXTMODE, STR_VUSBJOYSTICK_EXTMODE, g_model.usbJoystickExtMode, 0, 1, attr, event); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_IF_MODE: + g_model.usbJoystickIfMode = editChoice(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_USBJOYSTICK_IF_MODE, STR_VUSBJOYSTICK_IF_MODE, g_model.usbJoystickIfMode, 0, USBJOYS_LAST, attr, event); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_CIRC_CUTOUT: + g_model.usbJoystickCircularCut = editChoice(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_USBJOYSTICK_CIRC_COUTOUT, STR_VUSBJOYSTICK_CIRC_COUTOUT, g_model.usbJoystickCircularCut, 0, 2, attr, event); + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_CH_BUTTON: + lcdDrawText(INDENT_WIDTH, y, BUTTON(TR_USBJOYSTICK_SETTINGS), attr); + if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { + pushMenu(menuModelUSBJoystick); + } + break; + + case ITEM_MODEL_SETUP_USBJOYSTICK_APPLY: + lcdDrawText(INDENT_WIDTH, y, BUTTON(TR_USBJOYSTICK_APPLY_CHANGES), attr); + if (attr && !READ_ONLY() && event == EVT_KEY_FIRST(KEY_ENTER)) { + killEvents(event); + onUSBJoystickModelChanged(); + } + break; +#endif } } diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index 60398902e..c2d05b98b 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -222,3 +222,8 @@ add_gui_src(lvgl_widgets/input_mix_group.c) if(HARDWARE_TOUCH) add_gui_src(keyboard_curve.cpp) endif() + +if(USBJ_EX) + add_gui_src(model_usbjoystick.cpp) +endif() + diff --git a/radio/src/gui/colorlcd/bitmaps.cpp b/radio/src/gui/colorlcd/bitmaps.cpp index 90b887164..c2b4e517c 100644 --- a/radio/src/gui/colorlcd/bitmaps.cpp +++ b/radio/src/gui/colorlcd/bitmaps.cpp @@ -83,6 +83,9 @@ static const uint8_t mask_model_special_functions[] = { static const uint8_t mask_model_telemetry[] = { #include "mask_model_telemetry.lbm" }; +static const uint8_t mask_model_usb[] = { +#include "mask_model_usb.lbm" +}; static const uint8_t mask_menu_model_select[] = { #include "mask_menu_model_select.lbm" //TODO: someone may want to make proper icon }; @@ -343,6 +346,7 @@ static const _BuiltinIcon _builtinIcons[] = { {ICON_MODEL_SPECIAL_FUNCTIONS, mask_model_special_functions}, {ICON_MODEL_LUA_SCRIPTS, mask_model_lua_scripts}, {ICON_MODEL_TELEMETRY, mask_model_telemetry}, + {ICON_MODEL_USB, mask_model_usb}, {ICON_MODEL_SELECT, mask_menu_model_select}, {ICON_MODEL_SELECT_CATEGORY, mask_model_select_category}, {ICON_THEME, mask_menu_theme}, diff --git a/radio/src/gui/colorlcd/menus.h b/radio/src/gui/colorlcd/menus.h index 4d5350d1c..12fec9d90 100644 --- a/radio/src/gui/colorlcd/menus.h +++ b/radio/src/gui/colorlcd/menus.h @@ -56,6 +56,7 @@ enum MenuIcons { ICON_MODEL_SPECIAL_FUNCTIONS, ICON_MODEL_LUA_SCRIPTS, ICON_MODEL_TELEMETRY, + ICON_MODEL_USB, ICON_MODEL_SELECT, ICON_MODEL_SELECT_CATEGORY, ICON_THEME, diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 0e5295c58..fe86113a1 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -33,6 +33,10 @@ #include "throttle_params.h" #include "preflight_checks.h" +#if defined(USBJ_EX) +#include "model_usbjoystick.h" +#endif + #include #define SET_DIRTY() storageDirty(EE_MODEL) @@ -329,6 +333,15 @@ void ModelSetupPage::build(FormWindow * window) btn = new SubScreenButton(form, STR_THROTTLE_LABEL, []() { new ThrottleParams(); }); lv_obj_set_grid_cell(btn->getLvObj(), LV_GRID_ALIGN_STRETCH, 2, 1, LV_GRID_ALIGN_CENTER, 0, 1); + +#if defined(USBJ_EX) + form = new FormGroup(oform, rect_t{}); + form->setFlexLayout(LV_FLEX_FLOW_ROW, lv_dpx(PAGE_PADDING)); + lv_obj_set_grid_dsc_array(form->getLvObj(), col_dsc, row_dsc); + + btn = new SubScreenButton(form, STR_USBJOYSTICK_LABEL, []() { new ModelUSBJoystickPage(); }); + lv_obj_set_grid_cell(btn->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1); +#endif } #define MAX_SUBSCREEN_BTNS 9 diff --git a/radio/src/gui/colorlcd/model_usbjoystick.cpp b/radio/src/gui/colorlcd/model_usbjoystick.cpp new file mode 100644 index 000000000..1ab7fffd2 --- /dev/null +++ b/radio/src/gui/colorlcd/model_usbjoystick.cpp @@ -0,0 +1,601 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 for more details. + */ + +#include "model_usbjoystick.h" +#include "usb_joystick.h" +#include "channel_bar.h" +#include "button_matrix.h" +#include "lvgl_widgets/input_mix_line.h" + +#include "opentx.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) + +#if LCD_W > LCD_H // Landscape + +static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; + +#define USBCH_EDIT_STATUS_BAR_WIDTH 250 +#define USBCH_EDIT_STATUS_BAR_MARGIN 3 +#define USBCH_EDIT_RIGHT_MARGIN 0 +#define USBCH_COLS 4 + +static const lv_coord_t ch_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), + LV_GRID_FR(1), LV_GRID_FR(2), + LV_GRID_TEMPLATE_LAST}; + +#define USBCH_CHN_ROWS 1 +#define USBCH_BTN_MODE_COL 4 +#define USBCH_BTN_MODE_ROW 0 +#define USBCH_LINE_HEIGHT PAGE_LINE_HEIGHT + 14 + +static const lv_coord_t b_col_dsc[] = {LV_GRID_FR(10), 20, LV_GRID_FR(10), LV_GRID_FR(12), + LV_GRID_FR(9), LV_GRID_FR(9), LV_GRID_TEMPLATE_LAST}; + +static const lv_coord_t b_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; + +#else // Portrait + +static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; + +#define USBCH_EDIT_STATUS_BAR_WIDTH 160 +#define USBCH_EDIT_STATUS_BAR_MARGIN 0 +#define USBCH_EDIT_RIGHT_MARGIN 3 +#define USBCH_COLS 2 + +static const lv_coord_t ch_col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(3), + LV_GRID_TEMPLATE_LAST}; + +#define USBCH_CHN_ROWS 2 +#define USBCH_BTN_MODE_COL 2 +#define USBCH_BTN_MODE_ROW 1 +#define USBCH_LINE_HEIGHT 2 * PAGE_LINE_HEIGHT + 10 + +static const lv_coord_t b_col_dsc[] = {LV_GRID_FR(1), 20, LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; + +static const lv_coord_t b_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; + +#endif + +#define USBCH_BTNMX_COL 8 + +static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; + +class USBChannelEditStatusBar : public Window +{ + public: + USBChannelEditStatusBar(Window *parent, const rect_t &rect, int8_t channel) : + Window(parent, rect), _channel(channel) + { + channelBar = new ComboChannelBar(this, {USBCH_EDIT_STATUS_BAR_MARGIN, 0, rect.w - (USBCH_EDIT_STATUS_BAR_MARGIN * 2), rect.h}, channel); + channelBar->setLeftMargin(0); + channelBar->setTextColor(COLOR_THEME_PRIMARY2); + channelBar->setOutputChannelBarLimitColor(COLOR_THEME_EDIT); + } + + protected: + ComboChannelBar *channelBar; + int8_t _channel; +}; + +static void btnsel_event_cb(lv_event_t* e); + +class USBChannelButtonSel : public ButtonMatrix +{ + public: + USBChannelButtonSel(Window* parent, const rect_t& rect, uint8_t channel, + std::function _setValue) : + ButtonMatrix(parent, rect), + m_channel(channel), + m_setValue(std::move(_setValue)) + { + initBtnMap(USBCH_BTNMX_COL, USBJ_BUTTON_SIZE); + char snum[5]; + for (uint8_t btn = 0; btn < USBJ_BUTTON_SIZE; btn++) { + snprintf(snum, 5, "%u", btn); + setText(btn, snum); + } + update(); + + lv_obj_add_event_cb(lvobj, btnsel_event_cb, LV_EVENT_DRAW_PART_BEGIN, this); + + memset(m_btns, 0, USBJ_BUTTON_SIZE); + for (uint8_t ch = 0; ch < USBJ_MAX_JOYSTICK_CHANNELS; ch++) { + if (ch != m_channel) { + USBJoystickChData * cch = usbJChAddress(ch); + + if (cch->mode == USBJOYS_CH_BUTTON) { + uint8_t last = cch->lastBtnNum(); + for(uint8_t b = cch->btn_num; b <= last; b++) { + m_btns[b] = 1; + } + } + } + } + updateState(); + } + + void setValue(int val) + { + if (m_setValue) { + m_setValue(val); + } + } + + void onPress(uint8_t btn_id) override + { + setValue(btn_id); + updateState(); + } + + bool isActive(uint8_t btn_id) override { return false; } + + uint8_t getBtnState(uint8_t id) + { + if (id < USBJ_BUTTON_SIZE) return m_btns[id]; + return 0; + } + + void updateState() + { + USBJoystickChData * cch = usbJChAddress(m_channel); + + for(uint8_t i = 0; i < USBJ_BUTTON_SIZE; i++) m_btns[i] &= 1; + uint8_t last = cch->lastBtnNum(); + for(uint8_t b = cch->btn_num; b <= last; b++) m_btns[b] |= 2; + } + + protected: + uint8_t m_channel = 0; + std::function m_setValue; + uint8_t m_btns[USBJ_BUTTON_SIZE]; +}; + +static void btnsel_event_cb(lv_event_t* e) +{ + lv_event_code_t code = lv_event_get_code(e); + + if (code == LV_EVENT_DRAW_PART_BEGIN) { + lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e); + + if(dsc->class_p == &lv_btnmatrix_class && dsc->type == LV_BTNMATRIX_DRAW_PART_BTN) { + dsc->rect_dsc->radius = LV_RADIUS_CIRCLE; + auto btsel = (USBChannelButtonSel*)lv_event_get_user_data(e); + + uint8_t state = btsel->getBtnState((uint8_t)dsc->id); + if (state == 1) { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_DISABLED); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_PRIMARY1); + } + else if (state == 2) { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_EDIT); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_PRIMARY2); + } + else if (state == 3) { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_WARNING); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_PRIMARY2); + } + else { + dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_PRIMARY2); + dsc->label_dsc->color = makeLvColor(COLOR_THEME_SECONDARY1); + } + } + } +} + +#define USBCH_LINES 3 +#define SET_VALUE_WUPDATE(value) [=](int32_t newValue) { value = newValue; SET_DIRTY(); this->update(); } + +class USBChannelEditWindow : public Page +{ + public: + USBChannelEditWindow(uint8_t channel) : + Page(ICON_MODEL_USB), channel(channel) + { + auto form = new FormWindow(&body, rect_t{}); + form->padAll(4); + form->padLeft(8); + form->padRight(8); + + buildHeader(&header); + buildBody(form); + } + + protected: + uint8_t channel; + USBChannelEditStatusBar* statusBar = nullptr; + FormWindow* m_btnModeFrame = nullptr; + Window* m_axisModeLine = nullptr; + Window* m_simModeLine = nullptr; + USBChannelButtonSel* _BtnNumSel = nullptr; + StaticText* collisionText = nullptr; + Choice* m_btnPosChoice = nullptr; + + protected: + void update() + { + USBJoystickChData * cch = usbJChAddress(channel); + + lv_obj_add_flag(m_btnModeFrame->getLvObj(), LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(m_axisModeLine->getLvObj(), LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(m_simModeLine->getLvObj(), LV_OBJ_FLAG_HIDDEN); + if (cch->mode == USBJOYS_CH_BUTTON) + lv_obj_clear_flag(m_btnModeFrame->getLvObj(), LV_OBJ_FLAG_HIDDEN); + else if (cch->mode == USBJOYS_CH_AXIS) + lv_obj_clear_flag(m_axisModeLine->getLvObj(), LV_OBJ_FLAG_HIDDEN); + else if (cch->mode == USBJOYS_CH_SIM) + lv_obj_clear_flag(m_simModeLine->getLvObj(), LV_OBJ_FLAG_HIDDEN); + + if(m_btnPosChoice) + m_btnPosChoice->enable((cch->param != USBJOYS_BTN_MODE_SW_EMU) && (cch->param != USBJOYS_BTN_MODE_DELTA)); + + if(collisionText) { + collisionText->setText(""); + lv_obj_add_flag(collisionText->getLvObj(), LV_OBJ_FLAG_HIDDEN); + if (cch->mode == USBJOYS_CH_BUTTON) { + if (isUSBBtnNumCollision(channel)) { + collisionText->setText(STR_USBJOYSTICK_BTN_COLLISION); + lv_obj_clear_flag(collisionText->getLvObj(), LV_OBJ_FLAG_HIDDEN); + } + } + else if (cch->mode == USBJOYS_CH_AXIS) { + if (isUSBAxisCollision(channel)) { + collisionText->setText(STR_USBJOYSTICK_AXIS_COLLISION); + lv_obj_clear_flag(collisionText->getLvObj(), LV_OBJ_FLAG_HIDDEN); + } + } + else if (cch->mode == USBJOYS_CH_SIM) { + if (isUSBSimCollision(channel)) { + collisionText->setText(STR_USBJOYSTICK_AXIS_COLLISION); + lv_obj_clear_flag(collisionText->getLvObj(), LV_OBJ_FLAG_HIDDEN); + } + } + } + + _BtnNumSel->updateState(); + } + + void buildHeader(Window *window) + { + header.setTitle(STR_USBJOYSTICK_LABEL); + header.setTitle2(getSourceString(MIXSRC_CH1 + channel)); + + statusBar = new USBChannelEditStatusBar( + window, + {window->getRect().w - USBCH_EDIT_STATUS_BAR_WIDTH - USBCH_EDIT_RIGHT_MARGIN, + 0, USBCH_EDIT_STATUS_BAR_WIDTH, MENU_HEADER_HEIGHT + 3}, + channel); + } + + void buildBody(FormWindow* form) + { + FlexGridLayout grid(ch_col_dsc, row_dsc, 2); + form->setFlexLayout(); + + USBJoystickChData * cch = usbJChAddress(channel); + + auto line = form->newLine(&grid); + + line->padTop(0); + line->padBottom(0); + collisionText = new StaticText(line, rect_t{}, "", OPAQUE, FONT(BOLD) | COLOR_THEME_PRIMARY2 | CENTERED); + collisionText->setBackgroundColor(COLOR_THEME_WARNING); + lv_obj_set_grid_cell(collisionText->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, USBCH_COLS, LV_GRID_ALIGN_CENTER, 0, 1); + + line = form->newLine(&grid); + new StaticText(line, rect_t{}, STR_USBJOYSTICK_CH_MODE, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VUSBJOYSTICK_CH_MODE, 0, USBJOYS_CH_LAST, + GET_DEFAULT(cch->mode), SET_VALUE_WUPDATE(cch->mode)); + +#if LCD_H > LCD_W + line = form->newLine(&grid); +#endif + + new StaticText(line, rect_t{}, STR_USBJOYSTICK_CH_INVERSION, 0, COLOR_THEME_PRIMARY1); + new CheckBox(line, rect_t{}, GET_SET_DEFAULT(cch->inversion)); + + line = form->newLine(&grid); + m_btnModeFrame = new FormWindow(line, rect_t{}); + m_btnModeFrame->setFlexLayout(); + + line = m_btnModeFrame->newLine(&grid); + new StaticText(line, rect_t{}, STR_USBJOYSTICK_CH_BTNMODE, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VUSBJOYSTICK_CH_BTNMODE, 0, USBJOYS_BTN_MODE_LAST, + GET_DEFAULT(cch->param), + [=](int32_t newValue) + { + cch->param = newValue; + if(cch->param == USBJOYS_BTN_MODE_SW_EMU) m_btnPosChoice->setValue(0); + else if(cch->param == USBJOYS_BTN_MODE_DELTA) m_btnPosChoice->setValue(1); + SET_DIRTY(); + this->update(); + }); + +#if LCD_H > LCD_W + line = m_btnModeFrame->newLine(&grid); +#endif + + new StaticText(line, rect_t{}, STR_USBJOYSTICK_CH_SWPOS, 0, COLOR_THEME_PRIMARY1); + m_btnPosChoice = new Choice(line, rect_t{}, STR_VUSBJOYSTICK_CH_SWPOS, 0, 7, + GET_DEFAULT(cch->switch_npos), SET_VALUE_WUPDATE(cch->switch_npos)); + + line = m_btnModeFrame->newLine(&grid); + new StaticText(line, rect_t{}, STR_USBJOYSTICK_CH_BTNNUM, 0, COLOR_THEME_PRIMARY1); +#if LCD_H > LCD_W + line = m_btnModeFrame->newLine(&grid); +#endif + _BtnNumSel = new USBChannelButtonSel(line, rect_t{}, + channel, SET_VALUE_WUPDATE(cch->btn_num)); + + m_axisModeLine = form->newLine(&grid); + new StaticText(m_axisModeLine, rect_t{}, STR_USBJOYSTICK_CH_AXIS, 0, COLOR_THEME_PRIMARY1); + new Choice(m_axisModeLine, rect_t{}, STR_VUSBJOYSTICK_CH_AXIS, 0, USBJOYS_AXIS_LAST, + GET_DEFAULT(cch->param), SET_VALUE_WUPDATE(cch->param)); + + m_simModeLine = form->newLine(&grid); + new StaticText(m_simModeLine, rect_t{}, STR_USBJOYSTICK_CH_SIM, 0, COLOR_THEME_PRIMARY1); + new Choice(m_simModeLine, rect_t{}, STR_VUSBJOYSTICK_CH_SIM, 0, USBJOYS_SIM_LAST, + GET_DEFAULT(cch->param), SET_VALUE_WUPDATE(cch->param)); + + update(); + } +}; + +class USBChannelLineButton : public Button +{ + public: + USBChannelLineButton(Window* parent, uint8_t index) : + Button(parent, rect_t{}, nullptr, 0, 0, input_mix_line_create), index(index) + { + setHeight(USBCH_LINE_HEIGHT); + + lv_obj_set_layout(lvobj, LV_LAYOUT_GRID); + lv_obj_set_grid_dsc_array(lvobj, b_col_dsc, b_row_dsc); + lv_obj_set_style_pad_row(lvobj, 0, 0); + lv_obj_set_style_pad_column(lvobj, 4, 0); + + lv_obj_add_event_cb(lvobj, USBChannelLineButton::on_draw, + LV_EVENT_DRAW_MAIN_BEGIN, nullptr); + } + + static void on_draw(lv_event_t* e) + { + lv_obj_t* target = lv_event_get_target(e); + auto line = (USBChannelLineButton*)lv_obj_get_user_data(target); + if (line) { + if (!line->init) + line->delayed_init(e); + else + line->refresh(); + } + } + + void delayed_init(lv_event_t* e) + { + m_chn = lv_label_create(lvobj); + lv_obj_set_grid_cell(m_chn, LV_GRID_ALIGN_START, 0, 1, + LV_GRID_ALIGN_CENTER, 0, USBCH_CHN_ROWS); + + m_inverse = new StaticBitmap(this, rect_t{0, 0, 11, 16}, chanMonInvertedBitmap, COLOR_THEME_SECONDARY1, false); + lv_obj_set_grid_cell(m_inverse->getLvObj(), LV_GRID_ALIGN_START, 1, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + + m_mode = lv_label_create(lvobj); + lv_obj_set_grid_cell(m_mode, LV_GRID_ALIGN_START, 2, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + + m_param = lv_label_create(lvobj); + lv_obj_set_grid_cell(m_param, LV_GRID_ALIGN_START, 3, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + + m_btn_mode = lv_label_create(lvobj); + lv_obj_set_grid_cell(m_btn_mode, LV_GRID_ALIGN_START, USBCH_BTN_MODE_COL, 1, + LV_GRID_ALIGN_CENTER, USBCH_BTN_MODE_ROW, 1); + + m_btns = lv_label_create(lvobj); + lv_obj_set_grid_cell(m_btns, LV_GRID_ALIGN_START, USBCH_BTN_MODE_COL+1, 1, + LV_GRID_ALIGN_CENTER, USBCH_BTN_MODE_ROW, 1); + + lv_label_set_text(m_chn, getSourceString(MIXSRC_CH1 + index)); + lv_label_set_text(m_mode, ""); + lv_label_set_text(m_param, ""); + lv_label_set_text(m_btn_mode, ""); + lv_label_set_text(m_btns, ""); + + init = true; + refresh(); + lv_obj_update_layout(lvobj); + + if (e) { + auto param = lv_event_get_param(e); + lv_event_send(lvobj, LV_EVENT_DRAW_MAIN, param); + } + } + + void refresh() + { + if (!init) return; + + USBJoystickChData * cch = usbJChAddress(index); + + lv_label_set_text(m_mode, STR_VUSBJOYSTICK_CH_MODE[cch->mode]); + + if (cch->inversion) + lv_obj_clear_flag(m_inverse->getLvObj(), LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(m_inverse->getLvObj(), LV_OBJ_FLAG_HIDDEN); + + LcdFlags warn = COLOR_THEME_SECONDARY1; + LcdFlags font = FONT(STD); + const char* param = ""; + + if (cch->mode == USBJOYS_CH_BUTTON) { + param = STR_VUSBJOYSTICK_CH_BTNMODE[cch->param]; + } + else if (cch->mode == USBJOYS_CH_AXIS) { + param = STR_VUSBJOYSTICK_CH_AXIS[cch->param]; + if (isUSBAxisCollision(index)) { + warn = COLOR_THEME_WARNING; + font = FONT(BOLD); + } + } + else if (cch->mode == USBJOYS_CH_SIM) { + param = STR_VUSBJOYSTICK_CH_SIM[cch->param]; + if (isUSBSimCollision(index)) { + warn = COLOR_THEME_WARNING; + font = FONT(BOLD); + } + } + + lv_label_set_text(m_param, param); + lv_obj_set_style_text_color(m_param, makeLvColor(warn), 0); + lv_obj_set_style_text_font(m_param, getFont(font), 0); + + if (cch->mode == USBJOYS_CH_BUTTON) { + lv_label_set_text(m_btn_mode, STR_VUSBJOYSTICK_CH_SWPOS[cch->switch_npos]); + + char str[20]; + + uint8_t last = cch->lastBtnNum(); + if (last > cch->btn_num) + snprintf(str, 20, "%u..%u", cch->btn_num, last); + else + snprintf(str, 20, "%u", cch->btn_num); + lv_label_set_text(m_btns, str); + if (isUSBBtnNumCollision(index)) { + warn = COLOR_THEME_WARNING; + font = FONT(BOLD); + } + lv_obj_set_style_text_color(m_btns, makeLvColor(warn), 0); + lv_obj_set_style_text_font(m_btns, getFont(font), 0); + } else { + lv_label_set_text(m_btn_mode, ""); + lv_label_set_text(m_btns, ""); + } + } + + protected: + bool init = false; + uint8_t index; + + lv_obj_t* m_chn; + lv_obj_t* m_mode; + lv_obj_t* m_param; + lv_obj_t* m_btn_mode; + lv_obj_t* m_btns; + StaticBitmap* m_inverse; +}; + +ModelUSBJoystickPage::ModelUSBJoystickPage() : + Page(ICON_MODEL_USB) +{ + header.setTitle(STR_USBJOYSTICK_LABEL); + + auto form = new FormWindow(&body, rect_t{}); + form->padAll(lv_dpx(8)); + form->setFlexLayout(); + FlexGridLayout grid(line_col_dsc, row_dsc, 2); + + // Extended mode + auto line = form->newLine(&grid); + new StaticText(line, rect_t{}, STR_USBJOYSTICK_EXTMODE, 0, COLOR_THEME_PRIMARY1); + new Choice(line, rect_t{}, STR_VUSBJOYSTICK_EXTMODE, 0, 1, + GET_DEFAULT(g_model.usbJoystickExtMode), SET_VALUE_WUPDATE(g_model.usbJoystickExtMode)); + +#if LCD_H > LCD_W + line = form->newLine(&grid); +#endif + + _IfModeLabel = new StaticText(line, rect_t{}, STR_USBJOYSTICK_IF_MODE, 0, COLOR_THEME_PRIMARY1); + _IfMode = new Choice(line, rect_t{}, STR_VUSBJOYSTICK_IF_MODE, 0, USBJOYS_LAST, + GET_DEFAULT(g_model.usbJoystickIfMode), SET_VALUE_WUPDATE(g_model.usbJoystickIfMode)); + + line = form->newLine(&grid); + + _CircCoutoutLabel = new StaticText(line, rect_t{}, STR_USBJOYSTICK_CIRC_COUTOUT, 0, COLOR_THEME_PRIMARY1); + _CircCoutout = new Choice(line, rect_t{}, STR_VUSBJOYSTICK_CIRC_COUTOUT, 0, USBJOYS_LAST, + GET_DEFAULT(g_model.usbJoystickCircularCut), SET_VALUE_WUPDATE(g_model.usbJoystickCircularCut)); + +#if LCD_H > LCD_W + line = form->newLine(&grid); +#endif + + _ApplyBtn = new TextButton(line, rect_t{}, STR_USBJOYSTICK_APPLY_CHANGES, + [=]() { onUSBJoystickModelChanged(); this->update(); return 0; }); + + auto btngrp = new FormGroup(form, rect_t{}); + _ChannelsGroup = btngrp; + btngrp->setFlexLayout(); + btngrp->padRow(lv_dpx(4)); + for (uint8_t ch = 0; ch < USBJ_MAX_JOYSTICK_CHANNELS; ch++) { + + // Channel settings + auto btn = new USBChannelLineButton(btngrp, ch); + + USBJoystickChData * cch = usbJChAddress(ch); + btn->setPressHandler([=]() -> uint8_t { + if (cch->mode == USBJOYS_CH_NONE) { + editChannel(ch, btn); + } + else { + Menu *menu = new Menu(parent); + menu->addLine(STR_EDIT, [=]() { + editChannel(ch, btn); + }); + menu->addLine(STR_CLEAR, [=]() { + memset(cch, 0, sizeof(USBJoystickChData)); + SET_DIRTY(); + btn->invalidate(); + }); + } + return 0; + }); + } + + update(); +} + +void ModelUSBJoystickPage::update() +{ + const uint8_t usbj_ctrls = 6; + Window* ctrls[usbj_ctrls] = { _IfModeLabel, _IfMode, _CircCoutoutLabel, _CircCoutout, _ApplyBtn, _ChannelsGroup }; + + for(uint8_t i = 0; i < usbj_ctrls; i++) { + if(usbJoystickExtMode()) { + lv_obj_clear_flag(ctrls[i]->getLvObj(), LV_OBJ_FLAG_HIDDEN); + _ApplyBtn->enable(usbJoystickSettingsChanged()); + } + else { + lv_obj_add_flag(ctrls[i]->getLvObj(), LV_OBJ_FLAG_HIDDEN); + } + } +} + +void ModelUSBJoystickPage::editChannel(uint8_t channel, USBChannelLineButton* btn) +{ + auto chedit = new USBChannelEditWindow(channel); + chedit->setCloseHandler( + [=]() { this->update(); }); +} diff --git a/radio/src/gui/colorlcd/model_usbjoystick.h b/radio/src/gui/colorlcd/model_usbjoystick.h new file mode 100644 index 000000000..9d5e13cad --- /dev/null +++ b/radio/src/gui/colorlcd/model_usbjoystick.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 for more details. + */ + +#pragma once + +#include "page.h" + +class FormGroup; +class USBChannelLineButton; + +class ModelUSBJoystickPage : public Page +{ +public: + ModelUSBJoystickPage(); + +protected: + void update(); + void editChannel(uint8_t channel, USBChannelLineButton* btn); + +protected: + Window* _IfModeLabel; + Window* _IfMode; + Window* _CircCoutoutLabel; + Window* _CircCoutout; + TextButton* _ApplyBtn; + Window* _ChannelsGroup; +}; diff --git a/radio/src/gui/common/stdlcd/CMakeLists.txt b/radio/src/gui/common/stdlcd/CMakeLists.txt index 3c6096367..bf055f851 100644 --- a/radio/src/gui/common/stdlcd/CMakeLists.txt +++ b/radio/src/gui/common/stdlcd/CMakeLists.txt @@ -62,3 +62,11 @@ if(PXX2) ../common/stdlcd/model_receiver_options.cpp ) endif() + +if(USBJ_EX) + set(GUI_SRC + ${GUI_SRC} + ../common/stdlcd/model_usbjoystick.cpp + ) +endif() + diff --git a/radio/src/gui/common/stdlcd/model_usbjoystick.cpp b/radio/src/gui/common/stdlcd/model_usbjoystick.cpp new file mode 100644 index 000000000..dc5b2060d --- /dev/null +++ b/radio/src/gui/common/stdlcd/model_usbjoystick.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 for more details. + */ + +#include "opentx.h" +#include "usb_joystick.h" + +#define _STR_MAX(x) "/" #x +#define STR_MAX(x) _STR_MAX(x) + +#if LCD_W >= 212 + #define MODE_POS 5*FW + #define SUBMODE_POS MODE_POS+2*FW + #define USBJ_ONE_2ND_COLUMN (12*FW) + #define BTNMODE_POS SUBMODE_POS+5*FW + #define BTNNUM_POS BTNMODE_POS+12*FW +#else + #define MODE_POS 5*FW + #define SUBMODE_POS MODE_POS+2*FW + #define USBJ_ONE_2ND_COLUMN (12*FW) + #define BTNMODE_POS SUBMODE_POS+5*FW + #define BTNNUM_POS LCD_W +#endif + +enum USBJFields { + USBJ_FIELD_MODE, + USBJ_FIELD_INVERSION, + USBJ_FIELD_SUBMODE, + USBJ_FIELD_BTNPOS, + USBJ_FIELD_BTNNUM, + USBJ_FIELD_MAX +}; + +void menuModelUSBJoystickOne(event_t event) +{ +#if defined(KEYS_GPIO_REG_MDL) + if (event == EVT_KEY_FIRST(KEY_MODEL)) { + pushMenu(menuChannelsView); + killEvents(event); + } +#elif defined(NAVIGATION_X7) + if (event == EVT_KEY_FIRST(KEY_MENU)) { + pushMenu(menuChannelsView); + killEvents(event); + } +#elif defined(NAVIGATION_XLITE) + if (event == EVT_KEY_FIRST(KEY_ENTER) && IS_SHIFT_PRESSED()) { + pushMenu(menuChannelsView); + killEvents(event); + } +#endif + USBJoystickChData * cch = usbJChAddress(s_currIdx); + putsChn(12*FW, 0, s_currIdx+1, 0); + lcdDrawNumber(20*FW, 0, channelOutputs[s_currIdx], RIGHT); + + SUBMENU(STR_USBJOYSTICK_LABEL, USBJ_FIELD_MAX, { + 0, + (cch->mode != USBJOYS_CH_NONE) ? (uint8_t)0 : (uint8_t)HIDDEN_ROW, + (cch->mode != USBJOYS_CH_NONE) ? (uint8_t)0 : (uint8_t)HIDDEN_ROW, + (cch->mode == USBJOYS_CH_BUTTON) ? + (((cch->param == USBJOYS_BTN_MODE_SW_EMU) || (cch->param == USBJOYS_BTN_MODE_DELTA)) ? (uint8_t)READONLY_ROW : (uint8_t)0) + : (uint8_t)HIDDEN_ROW, + (cch->mode == USBJOYS_CH_BUTTON) ? (uint8_t)0 : (uint8_t)HIDDEN_ROW }); + + int8_t sub = menuVerticalPosition; + + char str[20]; + + for (uint8_t i=0; i<(NUM_BODY_LINES-1); i++) { + coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH; + uint8_t k = i + menuVerticalOffset; + for (int j=0; j<=k; ++j) { + if (j<(int)DIM(mstate_tab) && mstate_tab[j] == HIDDEN_ROW) { + ++k; + } + } + LcdFlags attr = (sub==k ? (s_editMode>0 ? BLINK|INVERS : INVERS) : 0); + + switch (k) { + case USBJ_FIELD_MODE: + cch->mode = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_MODE, STR_VUSBJOYSTICK_CH_MODE, cch->mode, 0, USBJOYS_CH_LAST, attr, event); + break; + + case USBJ_FIELD_INVERSION: + lcdDrawTextAlignedLeft(y, TR_USBJOYSTICK_CH_INVERSION); + drawCheckBox(USBJ_ONE_2ND_COLUMN, y, cch->inversion, attr); + if (attr) cch->inversion = checkIncDecModel(event, cch->inversion, 0, 1); + break; + + case USBJ_FIELD_SUBMODE: + if(cch->mode == USBJOYS_CH_BUTTON) { + cch->param = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_BTNMODE, STR_VUSBJOYSTICK_CH_BTNMODE, cch->param, 0, USBJOYS_BTN_MODE_LAST, attr, event); + if(cch->param == USBJOYS_BTN_MODE_SW_EMU) cch->switch_npos = 0; + else if(cch->param == USBJOYS_BTN_MODE_DELTA) cch->switch_npos = 1; + } + else if(cch->mode == USBJOYS_CH_AXIS) { + cch->param = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_AXIS, STR_VUSBJOYSTICK_CH_AXIS, cch->param, 0, USBJOYS_AXIS_LAST, attr, event); + } + else if(cch->mode == USBJOYS_CH_SIM) { + cch->param = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_SIM, STR_VUSBJOYSTICK_CH_SIM, cch->param, 0, USBJOYS_SIM_LAST, attr, event); + } + break; + + case USBJ_FIELD_BTNPOS: + if(cch->param == USBJOYS_BTN_MODE_SW_EMU) { + lcdDrawTextAlignedLeft(y, STR_USBJOYSTICK_CH_SWPOS); + lcdDrawText(USBJ_ONE_2ND_COLUMN, y, STR_VUSBJOYSTICK_CH_SWPOS[0], attr); + } + else if(cch->param == USBJOYS_BTN_MODE_DELTA) { + lcdDrawTextAlignedLeft(y, STR_USBJOYSTICK_CH_SWPOS); + lcdDrawText(USBJ_ONE_2ND_COLUMN, y, STR_VUSBJOYSTICK_CH_SWPOS[1], attr); + } + else { + cch->switch_npos = editChoice(USBJ_ONE_2ND_COLUMN, y, STR_USBJOYSTICK_CH_SWPOS, STR_VUSBJOYSTICK_CH_SWPOS, cch->switch_npos, 0, 7, attr, event); + } + break; + + case USBJ_FIELD_BTNNUM: + lcdDrawTextAlignedLeft(y, TR_USBJOYSTICK_CH_BTNNUM); + uint8_t last = cch->lastBtnNumNoCLip(); + uint8_t limited = 0; + if(last >= USBJ_BUTTON_SIZE) { + limited = 1; + last = USBJ_BUTTON_SIZE-1; + } + if (last > cch->btn_num) + snprintf(str, 20, "%u..%u%c", cch->btn_num, last, limited ? '!' : ' '); + else + snprintf(str, 20, "%u%c", cch->btn_num, limited ? '!' : ' '); + lcdDrawText(USBJ_ONE_2ND_COLUMN, y, str, attr); + if (attr) { + CHECK_INCDEC_MODELVAR(event, cch->btn_num, 0, USBJ_BUTTON_SIZE); + } + break; + } + } + + if (cch->mode == USBJOYS_CH_BUTTON) { + if (isUSBBtnNumCollision(s_currIdx)) { + lcdDrawText(LCD_W/2, LCD_H-FH, STR_USBJOYSTICK_BTN_COLLISION, INVERS | CENTERED); + } + } + else if (cch->mode == USBJOYS_CH_AXIS) { + if (isUSBAxisCollision(s_currIdx)) { + lcdDrawText(LCD_W/2, LCD_H-FH, STR_USBJOYSTICK_AXIS_COLLISION, INVERS | CENTERED); + } + } + else if (cch->mode == USBJOYS_CH_SIM) { + if (isUSBSimCollision(s_currIdx)) { + lcdDrawText(LCD_W/2, LCD_H-FH, STR_USBJOYSTICK_AXIS_COLLISION, INVERS | CENTERED); + } + } +} + +void onUSBJoystickMenu(const char *result) +{ + int8_t sub = menuVerticalPosition - HEADER_LINE; + USBJoystickChData * cs = usbJChAddress(sub); + + if (result == STR_EDIT) { + s_currIdx = sub; + pushMenu(menuModelUSBJoystickOne); + } + else if (result == STR_CLEAR) { + memset(cs, 0, sizeof(USBJoystickChData)); + storageDirty(EE_MODEL); + } +} + +void menuModelUSBJoystick(event_t event) +{ + check_submenu_simple(event, USBJ_MAX_JOYSTICK_CHANNELS - HEADER_LINE); + title(STR_USBJOYSTICK_LABEL); + + if (s_editMode > 0) s_editMode = 0; + + int8_t sub = menuVerticalPosition - HEADER_LINE; + + coord_t y = 0; + uint8_t k = 0; + + if (event == EVT_KEY_FIRST(KEY_ENTER)) { + killEvents(event); + USBJoystickChData * cch = usbJChAddress(sub); + s_currIdx = sub; + if (sub >= 0) + POPUP_MENU_ADD_ITEM(STR_EDIT); + if (cch->mode != USBJOYS_CH_NONE) + POPUP_MENU_ADD_ITEM(STR_CLEAR); + if (popupMenuItemsCount == 1) { + popupMenuItemsCount = 0; + s_currIdx = sub; + pushMenu(menuModelUSBJoystickOne); + } + else { + POPUP_MENU_START(onUSBJoystickMenu); + } + } + + char str[20]; + + for (uint8_t i=0; iinversion ? '{' : ' ', 0); + + lcdDrawTextAtIndex(MODE_POS, y, STR_VUSBJOYSTICK_CH_MODE_S, cch->mode, 0); + if (cch->mode == USBJOYS_CH_BUTTON) { + lcdDrawTextAtIndex(SUBMODE_POS, y, STR_VUSBJOYSTICK_CH_SWPOS, cch->switch_npos, 0); + lcdDrawTextAtIndex(BTNMODE_POS, y, STR_VUSBJOYSTICK_CH_BTNMODE_S, cch->param, 0); + + uint8_t last = cch->lastBtnNum(); + if (last > cch->btn_num) + sprintf(str, "%u..%u", cch->btn_num, last); + else + sprintf(str, "%u", cch->btn_num); + LcdFlags warn = 0; + if (isUSBBtnNumCollision(k)) warn = INVERS; + lcdDrawText(BTNNUM_POS, y, str, RIGHT|warn); + } + else if (cch->mode == USBJOYS_CH_AXIS) { + LcdFlags warn = 0; + if (isUSBAxisCollision(k)) warn = INVERS; + lcdDrawTextAtIndex(SUBMODE_POS, y, STR_VUSBJOYSTICK_CH_AXIS, cch->param, warn); + } + else if (cch->mode == USBJOYS_CH_SIM) { + LcdFlags warn = 0; + if (isUSBSimCollision(k)) warn = INVERS; + lcdDrawTextAtIndex(SUBMODE_POS, y, STR_VUSBJOYSTICK_CH_SIM, cch->param, warn); + } + } + +} diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index ce5d21f7b..c2d6e1536 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -293,6 +293,11 @@ LimitData *limitAddress(uint8_t idx) return &g_model.limitData[idx]; } +USBJoystickChData *usbJChAddress(uint8_t idx) +{ + return &g_model.usbJoystickCh[idx]; +} + void memswap(void * a, void * b, uint8_t size) { diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 22a294ea6..2859be7ad 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -690,6 +690,7 @@ ExpoData * expoAddress(uint8_t idx); MixData * mixAddress(uint8_t idx); LimitData * limitAddress(uint8_t idx); LogicalSwitchData * lswAddress(uint8_t idx); +USBJoystickChData * usbJChAddress(uint8_t idx); void applyDefaultTemplate(); void instantTrim(); diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 0c7c74668..7d2ecab02 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -35,6 +35,10 @@ using std::list; #endif +#if defined(USBJ_EX) +#include "usb_joystick.h" +#endif + #include #include "datastructs.h" @@ -1330,6 +1334,10 @@ void ModelsList::setCurrentModel(ModelCell *cell) gettime(&t); cell->lastOpened = gmktime(&t); modelslabels.setDirty(); + +#if defined(USBJ_EX) && defined(STM32) && !defined(SIMU) + onUSBJoystickModelChanged(); +#endif } /** diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index b67d776bb..ee0464375 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -22,6 +22,10 @@ #include "opentx.h" #include "timers_driver.h" +#if defined(USBJ_EX) +#include "usb_joystick.h" +#endif + #if defined(MULTIMODULE) #include "pulses/multi.h" #if defined(MULTI_PROTOLIST) @@ -214,6 +218,10 @@ void selectModel(uint8_t idx) g_eeGeneral.currModel = idx; storageDirty(EE_GENERAL); loadModel(idx); + +#if defined(USBJ_EX) && defined(STM32) && !defined(SIMU) + onUSBJoystickModelChanged(); +#endif } uint8_t findEmptyModel(uint8_t id, bool down) diff --git a/radio/src/storage/yaml/yaml_datastructs_boxer.cpp b/radio/src/storage/yaml/yaml_datastructs_boxer.cpp index bea7d7c58..921ad4032 100644 --- a/radio/src/storage/yaml/yaml_datastructs_boxer.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_boxer.cpp @@ -261,6 +261,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -812,6 +825,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -865,6 +887,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_lr3pro.cpp b/radio/src/storage/yaml/yaml_datastructs_lr3pro.cpp index f2b704a48..ff8b9db83 100644 --- a/radio/src/storage/yaml/yaml_datastructs_lr3pro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_lr3pro.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp index 94d24b4dd..d9b0f55bb 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp @@ -271,6 +271,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -842,6 +855,15 @@ static const struct YamlNode struct_TopBarPersistentData[] = { YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), @@ -895,6 +917,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_STRUCT("topbarData", 1248, struct_TopBarPersistentData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_t12.cpp b/radio/src/storage/yaml/yaml_datastructs_t12.cpp index fac8fdc7b..bab1b14cf 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t12.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_t8.cpp b/radio/src/storage/yaml/yaml_datastructs_t8.cpp index f2b704a48..ff8b9db83 100644 --- a/radio/src/storage/yaml/yaml_datastructs_t8.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_t8.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_tlite.cpp b/radio/src/storage/yaml/yaml_datastructs_tlite.cpp index f2b704a48..ff8b9db83 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tlite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tlite.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp index 8e2e29659..72177494f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tpro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tpro.cpp @@ -268,6 +268,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -817,6 +830,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -875,6 +897,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_UNSIGNED( "functionSwitchStartConfig", 16 ), YAML_UNSIGNED( "functionSwitchLogicalState", 8 ), YAML_ARRAY("switchNames", 24, 6, struct_string_24, NULL), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_tx12.cpp b/radio/src/storage/yaml/yaml_datastructs_tx12.cpp index 33e395dc6..2b1041a7a 100644 --- a/radio/src/storage/yaml/yaml_datastructs_tx12.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx12.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_tx12mk2.cpp b/radio/src/storage/yaml/yaml_datastructs_tx12mk2.cpp index b6a2140c4..d5cac5a67 100755 --- a/radio/src/storage/yaml/yaml_datastructs_tx12mk2.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_tx12mk2.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index c0a7e4642..b97b67893 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -302,6 +302,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -873,6 +886,15 @@ static const struct YamlNode struct_TopBarPersistentData[] = { YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), @@ -926,6 +948,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_STRUCT("topbarData", 2400, struct_TopBarPersistentData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp index 3c5239b9d..2312d3363 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp @@ -300,6 +300,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -871,6 +884,15 @@ static const struct YamlNode struct_TopBarPersistentData[] = { YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 1048, struct_ModelHeader, NULL), @@ -924,6 +946,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_STRUCT("topbarData", 2400, struct_TopBarPersistentData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x7.cpp b/radio/src/storage/yaml/yaml_datastructs_x7.cpp index f2b704a48..ff8b9db83 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x7.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x7.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp index dfefbcdbb..6798b9fb6 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9d.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9d.cpp @@ -267,6 +267,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -824,6 +837,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 192, struct_ModelHeader, NULL), @@ -879,6 +901,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp index f1edbb952..7e037858d 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9e.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9e.cpp @@ -306,6 +306,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -863,6 +876,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 192, struct_ModelHeader, NULL), @@ -919,6 +941,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp b/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp index 92c09ace6..75901cf5a 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9lite.cpp @@ -247,6 +247,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -796,6 +809,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -849,6 +871,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_x9lites.cpp b/radio/src/storage/yaml/yaml_datastructs_x9lites.cpp index 2bfffcae3..91e57fee2 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x9lites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x9lites.cpp @@ -255,6 +255,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -806,6 +819,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -859,6 +881,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp index cca9956f8..c99dd3adb 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlite.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlite.cpp @@ -252,6 +252,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -803,6 +816,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -856,6 +878,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp index 6239e463a..369606fc9 100644 --- a/radio/src/storage/yaml/yaml_datastructs_xlites.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_xlites.cpp @@ -254,6 +254,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -807,6 +820,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -860,6 +882,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/storage/yaml/yaml_datastructs_zorro.cpp b/radio/src/storage/yaml/yaml_datastructs_zorro.cpp index b6a2140c4..d5cac5a67 100644 --- a/radio/src/storage/yaml/yaml_datastructs_zorro.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_zorro.cpp @@ -260,6 +260,19 @@ const struct YamlIdStr enum_TelemetrySensorType[] = { { TELEM_TYPE_CALCULATED, "TYPE_CALCULATED" }, { 0, NULL } }; +const struct YamlIdStr enum_USBJoystickIfMode[] = { + { USBJOYS_JOYSTICK, "JOYSTICK" }, + { USBJOYS_GAMEPAD, "GAMEPAD" }, + { USBJOYS_MULTIAXIS, "MULTIAXIS" }, + { 0, NULL } +}; +const struct YamlIdStr enum_USBJoystickCh[] = { + { USBJOYS_CH_NONE, "CH_NONE" }, + { USBJOYS_CH_BUTTON, "CH_BUTTON" }, + { USBJOYS_CH_AXIS, "CH_AXIS" }, + { USBJOYS_CH_SIM, "CH_SIM" }, + { 0, NULL } +}; // // Structs last @@ -811,6 +824,15 @@ static const struct YamlNode struct_TelemetryScreenData[] = { YAML_UNION("u", 192, union_TelemetryScreenData_u_elmts, select_tele_screen_data), YAML_END }; +static const struct YamlNode struct_USBJoystickChData[] = { + YAML_IDX, + YAML_ENUM("mode", 3, enum_USBJoystickCh), + YAML_UNSIGNED( "inversion", 1 ), + YAML_UNSIGNED( "param", 4 ), + YAML_UNSIGNED( "btn_num", 5 ), + YAML_UNSIGNED( "switch_npos", 3 ), + YAML_END +}; static const struct YamlNode struct_ModelData[] = { YAML_CUSTOM("semver",nullptr,w_semver), YAML_STRUCT("header", 96, struct_ModelHeader, NULL), @@ -864,6 +886,10 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("screens", 192, 4, struct_TelemetryScreenData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), + YAML_UNSIGNED( "usbJoystickExtMode", 1 ), + YAML_ENUM("usbJoystickIfMode", 3, enum_USBJoystickIfMode), + YAML_UNSIGNED( "usbJoystickCircularCut", 4 ), + YAML_ARRAY("usbJoystickCh", 16, 26, struct_USBJoystickChData, NULL), YAML_END }; static const struct YamlNode struct_PartialModel[] = { diff --git a/radio/src/targets/common/arm/stm32/usb_driver.cpp b/radio/src/targets/common/arm/stm32/usb_driver.cpp index 0f378c1e5..4786a41c6 100644 --- a/radio/src/targets/common/arm/stm32/usb_driver.cpp +++ b/radio/src/targets/common/arm/stm32/usb_driver.cpp @@ -21,6 +21,10 @@ #include "usb_driver.h" +#if defined(USBJ_EX) +#include "usb_joystick.h" +#endif + extern "C" { #include "usb_conf.h" #include "usb_dcd_int.h" @@ -83,6 +87,9 @@ void usbStart() #if !defined(BOOT) case USB_JOYSTICK_MODE: // initialize USB as HID device +#if defined(USBJ_EX) + setupUSBJoystick(); +#endif USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_HID_cb, &USR_cb); break; #endif @@ -108,6 +115,18 @@ void usbStop() USBD_DeInit(&USB_OTG_dev); } +#if defined(USBJ_EX) +void usbJoystickRestart() +{ + if (getSelectedUsbMode() != USB_JOYSTICK_MODE) return; + + USBD_DeInit(&USB_OTG_dev); + DCD_DevDisconnect(&USB_OTG_dev); + DCD_DevConnect(&USB_OTG_dev); + USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_HID_cb, &USR_cb); +} +#endif + bool usbStarted() { return usbDriverStarted; @@ -125,6 +144,7 @@ bool usbStarted() */ void usbJoystickUpdate() { +#if !defined(USBJ_EX) static uint8_t HID_Buffer[HID_IN_PACKET]; // test to se if TX buffer is free @@ -158,5 +178,12 @@ void usbJoystickUpdate() } USBD_HID_SendReport(&USB_OTG_dev, HID_Buffer, HID_IN_PACKET); } +#else + // test to se if TX buffer is free + if (USBD_HID_SendReport(&USB_OTG_dev, 0, 0) == USBD_OK) { + usbReport_t ret = usbReport(); + USBD_HID_SendReport(&USB_OTG_dev, ret.ptr, ret.size); + } +#endif } #endif diff --git a/radio/src/targets/common/arm/stm32/usb_driver.h b/radio/src/targets/common/arm/stm32/usb_driver.h index bf3417f4a..e364ec7cf 100644 --- a/radio/src/targets/common/arm/stm32/usb_driver.h +++ b/radio/src/targets/common/arm/stm32/usb_driver.h @@ -48,6 +48,9 @@ int usbPlugged(); void usbInit(); void usbStart(); void usbStop(); +#if defined(USBJ_EX) +void usbJoystickRestart(); +#endif bool usbStarted(); EXTERN_C(int getSelectedUsbMode()); diff --git a/radio/src/targets/common/arm/stm32/usbd_conf.h b/radio/src/targets/common/arm/stm32/usbd_conf.h index 39b663734..85cfd9543 100644 --- a/radio/src/targets/common/arm/stm32/usbd_conf.h +++ b/radio/src/targets/common/arm/stm32/usbd_conf.h @@ -48,7 +48,9 @@ #define HID_IN_EP 0x81 #define HID_OUT_EP 0x01 +#if !defined(USBJ_EX) #define HID_IN_PACKET 19 +#endif #define HID_OUT_PACKET 9 #define CDC_IN_EP 0x81 /* EP1 for data IN */ diff --git a/radio/src/targets/common/arm/stm32/usbd_hid_joystick.c b/radio/src/targets/common/arm/stm32/usbd_hid_joystick.c index 1b194d650..116f6b992 100644 --- a/radio/src/targets/common/arm/stm32/usbd_hid_joystick.c +++ b/radio/src/targets/common/arm/stm32/usbd_hid_joystick.c @@ -22,6 +22,9 @@ #include "usbd_desc.h" #include "usbd_hid_core.h" #include "usbd_req.h" +#if defined(USBJ_EX) +#include "usb_joystick.h" +#endif /** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY * @{ @@ -88,6 +91,8 @@ static uint8_t USBD_HID_DataIn (void *pdev, uint8_t epnum); #pragma data_alignment=4 #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ + +#if !defined(USBJ_EX) /* This USB HID endpoint report description defines a device with: * 24 digital buttons @@ -140,7 +145,7 @@ __ALIGN_BEGIN static const uint8_t HID_JOYSTICK_ReportDesc[] __ALIGN_END = 0xc0, // END_COLLECTION 0xc0 // END_COLLECTION }; - +#endif /** @defgroup USBD_HID_Private_Variables * @{ @@ -191,7 +196,7 @@ __ALIGN_BEGIN static uint32_t USBD_HID_IdleState __ALIGN_END = 0; #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ /* USB HID device Configuration Descriptor */ -__ALIGN_BEGIN static const uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END = +__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END = { 0x09, /* bLength: Configuration Descriptor size */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */ @@ -225,7 +230,11 @@ __ALIGN_BEGIN static const uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __A 0x00, /*bCountryCode: Hardware target country*/ 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/ 0x22, /*bDescriptorType*/ +#if !defined(USBJ_EX) sizeof(HID_JOYSTICK_ReportDesc),/*wItemLength: Total length of Report descriptor*/ +#else + 0,/*wItemLength: Total length of Report descriptor*/ +#endif 0x00, /******************** Descriptor of Mouse endpoint ********************/ /* 27 */ @@ -234,7 +243,11 @@ __ALIGN_BEGIN static const uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __A HID_IN_EP, /*bEndpointAddress: Endpoint Address (IN)*/ 0x03, /*bmAttributes: Interrupt endpoint*/ +#if !defined(USBJ_EX) HID_IN_PACKET, /*wMaxPacketSize: 4 Byte max */ +#else + 0,/*HID_IN_PACKET,*/ /*wMaxPacketSize*/ +#endif 0x00, 0x01, /*bInterval: Polling Interval (1 ms)*/ /* 34 */ @@ -262,11 +275,18 @@ static uint8_t ReportSent; static uint8_t USBD_HID_Init (void *pdev, uint8_t cfgidx) { - +#if defined(USBJ_EX) + uint8_t hid_in_pkt_size = usbReportSize(); +#endif + /* Open EP IN */ DCD_EP_Open(pdev, HID_IN_EP, +#if !defined(USBJ_EX) HID_IN_PACKET, +#else + hid_in_pkt_size /*HID_IN_PACKET*/, +#endif USB_OTG_EP_INT); /* Open EP OUT */ @@ -349,8 +369,14 @@ static uint8_t USBD_HID_Setup (void *pdev, case USB_REQ_GET_DESCRIPTOR: if( req->wValue >> 8 == HID_REPORT_DESC) { +#if !defined(USBJ_EX) len = MIN(sizeof(HID_JOYSTICK_ReportDesc) , req->wLength); pbuf = HID_JOYSTICK_ReportDesc; // wiiccReportDescriptor; // +#else + struct usbReport_t ret = usbReportDesc(); + len = MIN(ret.size, req->wLength); + pbuf = ret.ptr; +#endif } else if( req->wValue >> 8 == HID_DESCRIPTOR_TYPE) { @@ -414,6 +440,12 @@ uint8_t USBD_HID_SendReport(USB_OTG_CORE_HANDLE *pdev, uint8_t * report, uint16 */ static const uint8_t *USBD_HID_GetCfgDesc (uint8_t speed, uint16_t *length) { +#if defined(USBJ_EX) + struct usbReport_t ret = usbReportDesc(); + USBD_HID_CfgDesc[25] = ret.size; + USBD_HID_CfgDesc[31] = usbReportSize(); +#endif + *length = sizeof (USBD_HID_CfgDesc); return USBD_HID_CfgDesc; } diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 8775e1bfd..bca436953 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -949,3 +949,31 @@ const char STR_MAIN_MENU_STATISTICS[] = TR_MAIN_MENU_STATISTICS; const char STR_MAIN_MENU_ABOUT_EDGETX[] = TR_MAIN_MENU_ABOUT_EDGETX; #endif + +#if defined(USBJ_EX) +const char STR_USBJOYSTICK_LABEL[] = TR_USBJOYSTICK_LABEL; +const char STR_USBJOYSTICK_EXTMODE[] = TR_USBJOYSTICK_EXTMODE; +ISTR(VUSBJOYSTICK_EXTMODE); +const char STR_USBJOYSTICK_SETTINGS[] = TR_USBJOYSTICK_SETTINGS; +const char STR_USBJOYSTICK_IF_MODE[] = TR_USBJOYSTICK_IF_MODE; +ISTR(VUSBJOYSTICK_IF_MODE); +const char STR_USBJOYSTICK_CH_MODE[] = TR_USBJOYSTICK_CH_MODE; +ISTR(VUSBJOYSTICK_CH_MODE); +ISTR(VUSBJOYSTICK_CH_MODE_S); +const char STR_USBJOYSTICK_CH_BTNMODE[] = TR_USBJOYSTICK_CH_BTNMODE; +ISTR(VUSBJOYSTICK_CH_BTNMODE); +ISTR(VUSBJOYSTICK_CH_BTNMODE_S); +const char STR_USBJOYSTICK_CH_SWPOS[] = TR_USBJOYSTICK_CH_SWPOS; +ISTR(VUSBJOYSTICK_CH_SWPOS); +const char STR_USBJOYSTICK_CH_AXIS[] = TR_USBJOYSTICK_CH_AXIS; +ISTR(VUSBJOYSTICK_CH_AXIS); +const char STR_USBJOYSTICK_CH_SIM[] = TR_USBJOYSTICK_CH_SIM; +ISTR(VUSBJOYSTICK_CH_SIM); +const char STR_USBJOYSTICK_CH_INVERSION[] = TR_USBJOYSTICK_CH_INVERSION; +const char STR_USBJOYSTICK_CH_BTNNUM[] = TR_USBJOYSTICK_CH_BTNNUM; +const char STR_USBJOYSTICK_BTN_COLLISION[] = TR_USBJOYSTICK_BTN_COLLISION; +const char STR_USBJOYSTICK_AXIS_COLLISION[] = TR_USBJOYSTICK_AXIS_COLLISION; +const char STR_USBJOYSTICK_CIRC_COUTOUT[] = TR_USBJOYSTICK_CIRC_COUTOUT; +ISTR(VUSBJOYSTICK_CIRC_COUTOUT); +const char STR_USBJOYSTICK_APPLY_CHANGES[] = TR_USBJOYSTICK_APPLY_CHANGES; +#endif diff --git a/radio/src/translations.h b/radio/src/translations.h index 6f56dcc52..d2d889eb8 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -1122,5 +1122,33 @@ extern const char STR_MAIN_MENU_ABOUT_EDGETX[]; #endif +#if defined(USBJ_EX) +extern const char STR_USBJOYSTICK_LABEL[]; +extern const char STR_USBJOYSTICK_EXTMODE[]; +extern const char* const STR_VUSBJOYSTICK_EXTMODE[]; +extern const char STR_USBJOYSTICK_SETTINGS[]; +extern const char STR_USBJOYSTICK_IF_MODE[]; +extern const char* const STR_VUSBJOYSTICK_IF_MODE[]; +extern const char STR_USBJOYSTICK_CH_MODE[]; +extern const char* const STR_VUSBJOYSTICK_CH_MODE[]; +extern const char* const STR_VUSBJOYSTICK_CH_MODE_S[]; +extern const char STR_USBJOYSTICK_CH_BTNMODE[]; +extern const char* const STR_VUSBJOYSTICK_CH_BTNMODE[]; +extern const char* const STR_VUSBJOYSTICK_CH_BTNMODE_S[]; +extern const char STR_USBJOYSTICK_CH_SWPOS[]; +extern const char* const STR_VUSBJOYSTICK_CH_SWPOS[]; +extern const char STR_USBJOYSTICK_CH_AXIS[]; +extern const char* const STR_VUSBJOYSTICK_CH_AXIS[]; +extern const char STR_USBJOYSTICK_CH_SIM[]; +extern const char* const STR_VUSBJOYSTICK_CH_SIM[]; +extern const char STR_USBJOYSTICK_CH_INVERSION[]; +extern const char STR_USBJOYSTICK_CH_BTNNUM[]; +extern const char STR_USBJOYSTICK_BTN_COLLISION[]; +extern const char STR_USBJOYSTICK_AXIS_COLLISION[]; +extern const char STR_USBJOYSTICK_CIRC_COUTOUT[]; +extern const char* const STR_VUSBJOYSTICK_CIRC_COUTOUT[]; +extern const char STR_USBJOYSTICK_APPLY_CHANGES[]; +#endif + #endif // _TRANSLATIONS_H_ diff --git a/radio/src/translations/cn.h b/radio/src/translations/cn.h index 1230148f8..59e9521bc 100644 --- a/radio/src/translations/cn.h +++ b/radio/src/translations/cn.h @@ -1237,3 +1237,29 @@ #define TR_VOICE_SWEDISH "瑞典语" #define TR_VOICE_TAIWANESE "中文-台湾国语" #define TR_VOICE_JAPANESE "日文" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/cz.h b/radio/src/translations/cz.h index ccca7d7ed..28e268be1 100644 --- a/radio/src/translations/cz.h +++ b/radio/src/translations/cz.h @@ -1239,3 +1239,29 @@ #define TR_VOICE_SWEDISH "Švédština" #define TR_VOICE_TAIWANESE "Tchajwanština" #define TR_VOICE_JAPANESE "Japonština" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/da.h b/radio/src/translations/da.h index 8d1794d28..d7f55298e 100644 --- a/radio/src/translations/da.h +++ b/radio/src/translations/da.h @@ -1252,3 +1252,29 @@ #define TR_VOICE_SWEDISH "Svensk" #define TR_VOICE_TAIWANESE "Taiwanesisk" #define TR_VOICE_JAPANESE "Japansk" + +#define TR_USBJOYSTICK_LABEL "USB joystik" +#define TR_USBJOYSTICK_EXTMODE "Tilstand" +#define TR_VUSBJOYSTICK_EXTMODE "Klassisk","Advanceret" +#define TR_USBJOYSTICK_SETTINGS "Kanal indstillinger" +#define TR_USBJOYSTICK_IF_MODE TR("Intf. tils.","Interface tilstand") +#define TR_VUSBJOYSTICK_IF_MODE "Joystik","Gamepad","MultiAkse" +#define TR_USBJOYSTICK_CH_MODE "Tilstand" +#define TR_VUSBJOYSTICK_CH_MODE "Ingen","Knap","Akse","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Knap tilstand" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positioner" +#define TR_VUSBJOYSTICK_CH_SWPOS "Skub","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Akse" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Skyder","Drej","Hjul" +#define TR_USBJOYSTICK_CH_SIM "Sim akse" +#define TR_VUSBJOYSTICK_CH_SIM "Kræ","Høj","Sid","Gas" +#define TR_USBJOYSTICK_CH_INVERSION "Omvendt" +#define TR_USBJOYSTICK_CH_BTNNUM "Knap #" +#define TR_USBJOYSTICK_BTN_COLLISION "!Knap # kollision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Akse kollision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Cirk. klip", "Cikulær klip") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "Ingen","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Gem ændringer" diff --git a/radio/src/translations/de.h b/radio/src/translations/de.h index 2dd06fa92..983f3dd08 100644 --- a/radio/src/translations/de.h +++ b/radio/src/translations/de.h @@ -1238,3 +1238,29 @@ #define TR_VOICE_SWEDISH "Schwedisch" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Japanisch" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/en.h b/radio/src/translations/en.h index ea9cdf3ed..97ffee0d8 100644 --- a/radio/src/translations/en.h +++ b/radio/src/translations/en.h @@ -1231,3 +1231,29 @@ #define TR_VOICE_SWEDISH "Swedish" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Japanese" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/es.h b/radio/src/translations/es.h index bc9beaae9..4d8dc4af0 100644 --- a/radio/src/translations/es.h +++ b/radio/src/translations/es.h @@ -1242,3 +1242,29 @@ #define TR_VOICE_SWEDISH "Swedish" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Japanese" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/fi.h b/radio/src/translations/fi.h index 447b47ed2..9a7211a48 100644 --- a/radio/src/translations/fi.h +++ b/radio/src/translations/fi.h @@ -1255,3 +1255,29 @@ #define TR_VOICE_SWEDISH "Swedish" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Japanese" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/fr.h b/radio/src/translations/fr.h index 4f959bb3a..64426be1e 100644 --- a/radio/src/translations/fr.h +++ b/radio/src/translations/fr.h @@ -1260,3 +1260,29 @@ #define TR_VOICE_SWEDISH "Swedish" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Japanese" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/it.h b/radio/src/translations/it.h index 6ad064b89..bec6b78e5 100644 --- a/radio/src/translations/it.h +++ b/radio/src/translations/it.h @@ -1247,3 +1247,29 @@ #define TR_VOICE_SWEDISH "Svedese" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Giapponese" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/jp.h b/radio/src/translations/jp.h index 3eed9ad17..317399cf8 100644 --- a/radio/src/translations/jp.h +++ b/radio/src/translations/jp.h @@ -1232,3 +1232,29 @@ #define TR_VOICE_SWEDISH "スウェーデン-SV" #define TR_VOICE_TAIWANESE "中文-TW" #define TR_VOICE_JAPANESE "日本-JP" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/nl.h b/radio/src/translations/nl.h index e34a8972b..20fe71e99 100644 --- a/radio/src/translations/nl.h +++ b/radio/src/translations/nl.h @@ -1252,3 +1252,29 @@ #define TR_VOICE_SWEDISH "Swedish" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Japanese" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/pl.h b/radio/src/translations/pl.h index 2b9f47fe3..f435ffd69 100644 --- a/radio/src/translations/pl.h +++ b/radio/src/translations/pl.h @@ -1236,3 +1236,29 @@ #define TR_VOICE_SWEDISH "Szwedzki" #define TR_VOICE_TAIWANESE "Tajwański" #define TR_VOICE_JAPANESE "Japonês" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/pt.h b/radio/src/translations/pt.h index 81957d7e8..c327708c7 100644 --- a/radio/src/translations/pt.h +++ b/radio/src/translations/pt.h @@ -1245,3 +1245,29 @@ #define TR_VOICE_SWEDISH "Swedish" #define TR_VOICE_TAIWANESE "Taiwanese" #define TR_VOICE_JAPANESE "Japanese" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/se.h b/radio/src/translations/se.h index 3410d9f52..329abcd18 100644 --- a/radio/src/translations/se.h +++ b/radio/src/translations/se.h @@ -1278,3 +1278,29 @@ #define TR_VOICE_SWEDISH "Svenska" #define TR_VOICE_TAIWANESE "Taiwanesiska" #define TR_VOICE_JAPANESE "Japanska" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/translations/tw.h b/radio/src/translations/tw.h index 20723b86e..ee120a57a 100644 --- a/radio/src/translations/tw.h +++ b/radio/src/translations/tw.h @@ -1235,3 +1235,29 @@ #define TR_VOICE_SWEDISH "瑞典語" #define TR_VOICE_TAIWANESE "中文-台灣國語" #define TR_VOICE_JAPANESE "日文" + +#define TR_USBJOYSTICK_LABEL "USB Joystick" +#define TR_USBJOYSTICK_EXTMODE "Mode" +#define TR_VUSBJOYSTICK_EXTMODE "Classic","Advanced" +#define TR_USBJOYSTICK_SETTINGS "Channel Settings" +#define TR_USBJOYSTICK_IF_MODE TR("If. mode","Interface mode") +#define TR_VUSBJOYSTICK_IF_MODE "Joystick","Gamepad","MultiAxis" +#define TR_USBJOYSTICK_CH_MODE "Mode" +#define TR_VUSBJOYSTICK_CH_MODE "None","Btn","Axis","Sim" +#define TR_VUSBJOYSTICK_CH_MODE_S "-","B","A","S" +#define TR_USBJOYSTICK_CH_BTNMODE "Button Mode" +#define TR_VUSBJOYSTICK_CH_BTNMODE "Normal","Pulse","SWEmu","Delta","Companion" +#define TR_VUSBJOYSTICK_CH_BTNMODE_S TR("Norm","Normal"),TR("Puls","Pulse"),TR("SWEm","SWEmul"),TR("Delt","Delta"),TR("CPN","Companion") +#define TR_USBJOYSTICK_CH_SWPOS "Positions" +#define TR_VUSBJOYSTICK_CH_SWPOS "Push","2POS","3POS","4POS","5POS","6POS","7POS","8POS" +#define TR_USBJOYSTICK_CH_AXIS "Axis" +#define TR_VUSBJOYSTICK_CH_AXIS "X","Y","Z","rotX","rotY","rotZ","Slider","Dial","Wheel" +#define TR_USBJOYSTICK_CH_SIM "Sim axis" +#define TR_VUSBJOYSTICK_CH_SIM "Ail","Ele","Rud","Thr" +#define TR_USBJOYSTICK_CH_INVERSION "Inversion" +#define TR_USBJOYSTICK_CH_BTNNUM "Button no." +#define TR_USBJOYSTICK_BTN_COLLISION "!Button no. collision!" +#define TR_USBJOYSTICK_AXIS_COLLISION "!Axis collision!" +#define TR_USBJOYSTICK_CIRC_COUTOUT TR("Circ. cut", "Circular cutout") +#define TR_VUSBJOYSTICK_CIRC_COUTOUT "None","X-Y, Z-rX","X-Y, rX-rY" +#define TR_USBJOYSTICK_APPLY_CHANGES "Apply changes" diff --git a/radio/src/usb_joystick.cpp b/radio/src/usb_joystick.cpp new file mode 100644 index 000000000..047d3009f --- /dev/null +++ b/radio/src/usb_joystick.cpp @@ -0,0 +1,758 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 for more details. + */ + +#include "opentx.h" +#include "usb_joystick.h" + +#define MAX_HID_REPORTDESC 160 +#define MAX_HID_REPORT 80 + +uint8_t _hidReportDesc[MAX_HID_REPORTDESC] = { }; +uint8_t _hidReportDescSize = 0; +uint8_t _hidReport[MAX_HID_REPORT] = { }; +uint8_t _hidReportSize = 0; + +static uint8_t _buttonState[USBJ_BUTTON_SIZE >> 3] = { }; + +static uint16_t _usbLastChannelOutput[USBJ_MAX_JOYSTICK_CHANNELS] = { }; +static uint8_t _usbChannelTimerActive[USBJ_MAX_JOYSTICK_CHANNELS] = { }; +static uint8_t _usbChannelTimer[USBJ_MAX_JOYSTICK_CHANNELS] = { }; + +static uint8_t _usbJoystickIfMode = 0; +static uint8_t _usbJoystickCircularCut = 0; +static USBJoystickChData _usbJoystickCh[USBJ_MAX_JOYSTICK_CHANNELS]; + +#define USBJ_CC_XYZrX 1 +#define USBJ_CC_XYrXrZ 2 + +static uint8_t _usbJoystickButtonChannels[USBJ_MAX_JOYSTICK_CHANNELS] = { }; +static uint8_t _usbJoystickButtonCount = 0; +static uint8_t _usbJoystickAxisChannels[USBJ_MAX_JOYSTICK_CHANNELS] = { }; +static uint8_t _usbJoystickAxisCount = 0; + +static uint8_t _usbJoystickAxisPairs[3][2] = { }; + +static const uint8_t _usbJoystickUniqueAxis[USBJOYS_AXIS_LAST + 1] = + { 1, 1, 1, 1, 1, 1, 0, 0, 0 }; + +static const uint8_t _usbJoystickUniqueSims[USBJOYS_SIM_LAST + 1] = + { 1, 1, 1, 1 }; + +/* + This USB HID endpoint report description defines a device with: + * 24 digital buttons + * 8 analog axes with 8bit resolution + + Repot packet described as C struct is: + + struct { + uint8_t buttons1; //bit 0 - button 1, bit 1 - button 2, ..., mapped to channels 9-16, on if channel > 0 + uint8_t buttons2; // mapped to channels 17-24, on if channel > 0 + uint8_t buttons3; // mapped to channels 25-32, on if channel > 0 + uint8_t X; //analog value, mapped to channel 1 + uint8_t Y; //analog value, mapped to channel 2 + uint8_t Z; //analog value, mapped to channel 3 + uint8_t Rx; //analog value, mapped to channel 4 + uint8_t Ry //analog value, mapped to channel 5 + uint8_t Rz; //analog value, mapped to channel 6 + uint8_t S1; //analog value, mapped to channel 7 + uint8_t S2; //analog value, mapped to channel 8 + } +*/ +static const uint8_t HID_JOYSTICK_ReportDesc[] = +{ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x05, // USAGE (Game Pad) + 0xa1, 0x01, // COLLECTION (Application) + 0xa1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x18, // USAGE_MAXIMUM (Button 24) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x18, // REPORT_COUNT (24) + 0x75, 0x01, // REPORT_SIZE (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x32, // USAGE (Z) + 0x09, 0x33, // USAGE (Rx) + 0x09, 0x34, // USAGE (Ry) + 0x09, 0x35, // USAGE (Rz) + 0x09, 0x36, // USAGE (Slider) + 0x09, 0x36, // USAGE (Slider) + 0x16, 0x00, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xFF, 0x07, // LOGICAL_MAXIMUM (2047) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0xc0, // END_COLLECTION + 0xc0 // END_COLLECTION +}; + +int usbJoystickExtMode() +{ + return g_model.usbJoystickExtMode; +} + +int usbJoystickActive() +{ + return (usbPlugged() && getSelectedUsbMode() == USB_JOYSTICK_MODE); +} + +int usbJoystickSettingsChanged() +{ + if (!usbJoystickActive()) return false; + + if((_usbJoystickIfMode != g_model.usbJoystickIfMode) + || (_usbJoystickCircularCut != g_model.usbJoystickCircularCut)) return true; + if(memcmp(_usbJoystickCh, g_model.usbJoystickCh, sizeof(g_model.usbJoystickCh)) != 0) return true; + + return false; +} + +int setupUSBJoystick() +{ + static const uint8_t axisTypeCodes[USBJOYS_AXIS_LAST + 1] = + { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 }; + static const uint8_t simTypeCodes[USBJOYS_SIM_LAST + 1] = + { 0xb0, 0xb8, 0xba, 0xbb }; + + static uint8_t oldHIDReportDesc[MAX_HID_REPORTDESC] = { }; + static uint8_t oldHIDReportDescSize = 0; + + // copy old to compare + memcpy(oldHIDReportDesc, _hidReportDesc, MAX_HID_REPORTDESC); + oldHIDReportDescSize = _hidReportDescSize; + + // copy the model snapshot + _usbJoystickIfMode = g_model.usbJoystickIfMode; + _usbJoystickCircularCut = g_model.usbJoystickCircularCut; + memcpy(_usbJoystickCh, g_model.usbJoystickCh, sizeof(g_model.usbJoystickCh)); + + // Init data + memset(_hidReportDesc, 0, MAX_HID_REPORTDESC); + _hidReportDescSize = 0; + memset(_hidReport, 0, MAX_HID_REPORT); + _hidReportSize = 0; + memset(_buttonState, 0, (USBJ_BUTTON_SIZE >> 3)); + memset(_usbLastChannelOutput, 0xff, sizeof(_usbLastChannelOutput)); + memset(_usbChannelTimer, 0, sizeof(_usbChannelTimer)); + memset(_usbChannelTimerActive, 0, sizeof(_usbChannelTimerActive)); + + memset(_usbJoystickButtonChannels, 0, USBJ_MAX_JOYSTICK_CHANNELS); + _usbJoystickButtonCount = 0; + memset(_usbJoystickAxisChannels, 0, USBJ_MAX_JOYSTICK_CHANNELS); + _usbJoystickAxisCount = 0; + memset(_usbJoystickAxisPairs, 0xff, sizeof(_usbJoystickAxisPairs)); + + if (!usbJoystickExtMode()) { + // Classic USB Joystick report description + memcpy(_hidReportDesc, HID_JOYSTICK_ReportDesc, sizeof(HID_JOYSTICK_ReportDesc)); + _hidReportDescSize = sizeof(HID_JOYSTICK_ReportDesc); + _hidReportSize = 19; + + } else { + + uint8_t genAxisCount = 0; + uint8_t simAxisCount = 0; + + // sort channels by type + uint8_t typeCount[USBJOYS_CH_LAST + 1] = { }; + uint8_t axisCount[USBJOYS_AXIS_LAST + 1] = { }; + uint8_t simCount[USBJOYS_SIM_LAST + 1] = { }; + uint8_t axisIx[USBJOYS_AXIS_LAST + 1] = { }; + uint8_t simIx[USBJOYS_SIM_LAST + 1] = { }; + uint8_t mode = 0; + uint8_t submode = 0; + for (uint8_t i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) { + mode = _usbJoystickCh[i].mode; + + if (_usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) { + typeCount[mode]++; + } + else if (_usbJoystickCh[i].mode == USBJOYS_CH_AXIS) { + submode = _usbJoystickCh[i].param; + if (submode <= USBJOYS_AXIS_LAST) { + if ((_usbJoystickUniqueAxis[submode] == 0) || (axisCount[submode] == 0)) { + axisCount[submode]++; + typeCount[mode]++; + } + } + } + else if (_usbJoystickCh[i].mode == USBJOYS_CH_SIM) { + submode = _usbJoystickCh[i].param; + if (submode <= USBJOYS_SIM_LAST) { + if ((_usbJoystickUniqueSims[submode] == 0) || (simCount[submode] == 0)) { + simCount[submode]++; + typeCount[mode]++; + } + } + } + } + uint8_t ixcount = 0; + for (uint8_t i = 0; i <= USBJOYS_AXIS_LAST; i++) { + axisIx[i] = ixcount; + ixcount += axisCount[i]; + } + ixcount = 0; + for (uint8_t i = 0; i <= USBJOYS_SIM_LAST; i++) { + simIx[i] = ixcount; + ixcount += simCount[i]; + } + ixcount = 0; + for (uint8_t i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) { + mode = _usbJoystickCh[i].mode; + + if (_usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) { + _usbJoystickButtonChannels[ixcount] = i; + ixcount++; + } + else if (_usbJoystickCh[i].mode == USBJOYS_CH_AXIS) { + submode = _usbJoystickCh[i].param; + if (submode <= USBJOYS_AXIS_LAST) { + _usbJoystickAxisChannels[axisIx[submode]] = i; + if (_usbJoystickUniqueAxis[submode] == 0) { + axisIx[submode]++; + } + // save axis pairs + if(submode == USBJOYS_AXIS_X) { + _usbJoystickAxisPairs[0][0] = i; + } + else if(submode == USBJOYS_AXIS_Y) { + _usbJoystickAxisPairs[0][1] = i; + } + else if(submode == USBJOYS_AXIS_Z) { + _usbJoystickAxisPairs[1][0] = i; + } + else if(submode == USBJOYS_AXIS_RX) { + _usbJoystickAxisPairs[1][1] = i; + _usbJoystickAxisPairs[2][0] = i; + } + else if(submode == USBJOYS_AXIS_RY) { + _usbJoystickAxisPairs[2][1] = i; + } + } + } + else if (_usbJoystickCh[i].mode == USBJOYS_CH_SIM) { + submode = _usbJoystickCh[i].param; + if (submode <= USBJOYS_SIM_LAST) { + _usbJoystickAxisChannels[simIx[submode]] = i; + if (_usbJoystickUniqueSims[submode] == 0) { + simIx[submode]++; + } + } + } + } + + // store counts + _usbJoystickButtonCount = typeCount[USBJOYS_CH_BUTTON]; + genAxisCount = typeCount[USBJOYS_CH_AXIS]; + simAxisCount = typeCount[USBJOYS_CH_SIM]; + _usbJoystickAxisCount = typeCount[USBJOYS_CH_AXIS] + typeCount[USBJOYS_CH_SIM]; + + // generate report desc + + // USAGE_PAGE (Generic Desktop) + _hidReportDesc[_hidReportDescSize++] = 0x05; + _hidReportDesc[_hidReportDescSize++] = 0x01; + + // USAGE (Joystick=0x04, Gamepad=0x05, Multi-axis Controller=0x08) + uint8_t joystickType = 0x04; + if (_usbJoystickIfMode == USBJOYS_GAMEPAD) joystickType = 0x05; + else if (_usbJoystickIfMode == USBJOYS_MULTIAXIS) joystickType = 0x08; + _hidReportDesc[_hidReportDescSize++] = 0x09; + _hidReportDesc[_hidReportDescSize++] = joystickType; + + // COLLECTION (Application) + _hidReportDesc[_hidReportDescSize++] = 0xa1; + _hidReportDesc[_hidReportDescSize++] = 0x01; + + // COLLECTION (Physical) + _hidReportDesc[_hidReportDescSize++] = 0xa1; + _hidReportDesc[_hidReportDescSize++] = 0x00; + + // REPORT_ID (1) +// _hidReportDesc[_hidReportDescSize++] = 0x85; +// _hidReportDesc[_hidReportDescSize++] = 0x01; + + // button types + + // USAGE_PAGE (Button) + _hidReportDesc[_hidReportDescSize++] = 0x05; + _hidReportDesc[_hidReportDescSize++] = 0x09; + + // USAGE_MINIMUM (Button 1) + _hidReportDesc[_hidReportDescSize++] = 0x19; + _hidReportDesc[_hidReportDescSize++] = 0x01; + + // USAGE_MAXIMUM (Button USBJ_BUTTON_SIZE) + _hidReportDesc[_hidReportDescSize++] = 0x29; + _hidReportDesc[_hidReportDescSize++] = USBJ_BUTTON_SIZE; + + // LOGICAL_MINIMUM (0) + _hidReportDesc[_hidReportDescSize++] = 0x15; + _hidReportDesc[_hidReportDescSize++] = 0x00; + + // LOGICAL_MAXIMUM (1) + _hidReportDesc[_hidReportDescSize++] = 0x25; + _hidReportDesc[_hidReportDescSize++] = 0x01; + + // REPORT_COUNT (#) + _hidReportDesc[_hidReportDescSize++] = 0x95; + _hidReportDesc[_hidReportDescSize++] = USBJ_BUTTON_SIZE; + + // REPORT_SIZE (1) + _hidReportDesc[_hidReportDescSize++] = 0x75; + _hidReportDesc[_hidReportDescSize++] = 0x01; + + // INPUT (Data,Var,Abs,Preferred) + _hidReportDesc[_hidReportDescSize++] = 0x81; + _hidReportDesc[_hidReportDescSize++] = 0x02; + + // generic axis types + if (genAxisCount > 0) { + + // USAGE_PAGE (Generic Desktop) + _hidReportDesc[_hidReportDescSize++] = 0x05; + _hidReportDesc[_hidReportDescSize++] = 0x01; + + for (uint8_t i = 0; i <= USBJOYS_AXIS_LAST; i++) { + for (uint8_t cnt = 0; cnt < axisCount[i]; cnt++) { + // USAGE (#) + _hidReportDesc[_hidReportDescSize++] = 0x09; + _hidReportDesc[_hidReportDescSize++] = axisTypeCodes[i]; + } + } + + // LOGICAL_MINIMUM (0) + _hidReportDesc[_hidReportDescSize++] = 0x16; + _hidReportDesc[_hidReportDescSize++] = 0x00; + _hidReportDesc[_hidReportDescSize++] = 0x00; + + // LOGICAL_MAXIMUM (2047) + _hidReportDesc[_hidReportDescSize++] = 0x26; + _hidReportDesc[_hidReportDescSize++] = 0xFF; + _hidReportDesc[_hidReportDescSize++] = 0x07; + + // REPORT_SIZE (16) + _hidReportDesc[_hidReportDescSize++] = 0x75; + _hidReportDesc[_hidReportDescSize++] = 0x10; + + // REPORT_COUNT (#) + _hidReportDesc[_hidReportDescSize++] = 0x95; + _hidReportDesc[_hidReportDescSize++] = genAxisCount; + + // INPUT (Data,Var,Abs) + _hidReportDesc[_hidReportDescSize++] = 0x81; + _hidReportDesc[_hidReportDescSize++] = 0x02; + } + + // sim axis types + if (simAxisCount > 0) { + + // USAGE_PAGE (Simulation Controls) + _hidReportDesc[_hidReportDescSize++] = 0x05; + _hidReportDesc[_hidReportDescSize++] = 0x02; + + for (uint8_t i = 0; i <= USBJOYS_SIM_LAST; i++) { + for (uint8_t cnt = 0; cnt < simCount[i]; cnt++) { + // USAGE (#) + _hidReportDesc[_hidReportDescSize++] = 0x09; + _hidReportDesc[_hidReportDescSize++] = simTypeCodes[i]; + } + } + + // LOGICAL_MINIMUM (0) + _hidReportDesc[_hidReportDescSize++] = 0x16; + _hidReportDesc[_hidReportDescSize++] = 0x00; + _hidReportDesc[_hidReportDescSize++] = 0x00; + + // LOGICAL_MAXIMUM (2047) + _hidReportDesc[_hidReportDescSize++] = 0x26; + _hidReportDesc[_hidReportDescSize++] = 0xFF; + _hidReportDesc[_hidReportDescSize++] = 0x07; + + // REPORT_SIZE (16) + _hidReportDesc[_hidReportDescSize++] = 0x75; + _hidReportDesc[_hidReportDescSize++] = 0x10; + + // REPORT_COUNT (#) + _hidReportDesc[_hidReportDescSize++] = 0x95; + _hidReportDesc[_hidReportDescSize++] = simAxisCount; + + // INPUT (Data,Var,Abs) + _hidReportDesc[_hidReportDescSize++] = 0x81; + _hidReportDesc[_hidReportDescSize++] = 0x02; + } + + // END_COLLECTION + _hidReportDesc[_hidReportDescSize++] = 0xc0; + + // END_COLLECTION + _hidReportDesc[_hidReportDescSize++] = 0xc0; + + // end of report desc + + _hidReportSize = (USBJ_BUTTON_SIZE / 8) + (_usbJoystickAxisCount * 2); + } + + //compare with the old description + if (_hidReportDescSize != oldHIDReportDescSize) return true; + if (memcmp(_hidReportDesc, oldHIDReportDesc, _hidReportDescSize) != 0) return true; + + return false; +} + +extern "C" struct usbReport_t usbReportDesc() +{ + usbReport_t res = { _hidReportDesc, _hidReportDescSize }; + return res; +} + +void usbClassicStateUpdate() +{ + memset(_hidReport, 0, MAX_HID_REPORT); + + //buttons + _hidReport[0] = 0; + _hidReport[1] = 0; + _hidReport[2] = 0; + for (int i = 0; i < 8; ++i) { + if ( channelOutputs[i+8] > 0 ) { + _hidReport[0] |= (1 << i); + } + if ( channelOutputs[i+16] > 0 ) { + _hidReport[1] |= (1 << i); + } + if ( channelOutputs[i+24] > 0 ) { + _hidReport[2] |= (1 << i); + } + } + + //analog values + //uint8_t * p = HID_Buffer + 1; + for (int i = 0; i < 8; ++i) { + + int16_t value = channelOutputs[i] + 1024; + if ( value > 2047 ) value = 2047; + else if ( value < 0 ) value = 0; + _hidReport[i*2 +3] = static_cast(value & 0xFF); + _hidReport[i*2 +4] = static_cast((value >> 8) & 0x07); + + } +} + +static void setBtnBits(uint8_t ix, uint8_t value, uint8_t size) +{ + for(uint8_t i = 0; i < size; i++) { + uint8_t bitix = ix + i; + if(bitix >= USBJ_BUTTON_SIZE) break; + + if(value & (1 << i)) { + _buttonState[(bitix / 8)] |= 1 << (bitix % 8); + } + else { + _buttonState[(bitix / 8)] &= ~(1 << (bitix % 8)); + } + } +} + +static void toggleBtnBit(uint8_t bitix) +{ + if(bitix >= USBJ_BUTTON_SIZE) return; + _buttonState[(bitix / 8)] ^= 1 << (bitix % 8); +} + +#define g_usbTmr10ms (*(uint8_t*)&g_tmr10ms) +#define BTNPUSH_TIME 20 /* x10ms */ + +static int16_t circularCutoutValue(uint8_t chix) +{ + int32_t value = channelOutputs[chix]; + uint8_t chpair = 0xff; + const int32_t limit = 1048576; /* 1024^2 */ + + if(_usbJoystickCircularCut == USBJ_CC_XYZrX) { + if(chix == _usbJoystickAxisPairs[0][0]) chpair = _usbJoystickAxisPairs[0][1]; + else if(chix == _usbJoystickAxisPairs[0][1]) chpair = _usbJoystickAxisPairs[0][0]; + else if(chix == _usbJoystickAxisPairs[1][0]) chpair = _usbJoystickAxisPairs[1][1]; + else if(chix == _usbJoystickAxisPairs[1][1]) chpair = _usbJoystickAxisPairs[1][0]; + } + else if(_usbJoystickCircularCut == USBJ_CC_XYrXrZ) { + if(chix == _usbJoystickAxisPairs[0][0]) chpair = _usbJoystickAxisPairs[0][1]; + else if(chix == _usbJoystickAxisPairs[0][1]) chpair = _usbJoystickAxisPairs[0][0]; + else if(chix == _usbJoystickAxisPairs[2][0]) chpair = _usbJoystickAxisPairs[2][1]; + else if(chix == _usbJoystickAxisPairs[2][1]) chpair = _usbJoystickAxisPairs[2][0]; + } + + if(chpair != 0xff) { + int32_t pval = channelOutputs[chpair]; + int32_t sum = ((value*value) + (pval*pval)); + if(sum > limit) { + double ratio = sqrt((double)limit / (double)sum); + value *= ratio; + } + } + + return value; +} + +void usbStateUpdate() +{ + const uint8_t button_ix = 0; + const uint8_t axis_ix = button_ix + (USBJ_BUTTON_SIZE >> 3); + + memset(_hidReport, 0, MAX_HID_REPORT); + + uint8_t chix = 0; + + int16_t value = 0; + + uint8_t swpos = 0; + uint8_t btnval = 0; + + for (uint8_t i = 0; i < _usbJoystickButtonCount; i++) { + chix = _usbJoystickButtonChannels[i]; + + value = channelOutputs[chix] + 1024; + if ( value > 2047 ) value = 2047; + else if ( value < 0 ) value = 0; + + if (_usbJoystickCh[chix].inversion) value = 2047 - value; + + swpos = _usbJoystickCh[chix].switch_npos + 1; + + // Quantization of the values + if ((_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_NORMAL) + || (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE)) { + + if (swpos == 1) { + btnval = (value > 1024); + } else { + btnval = static_cast(value) / (2048 / swpos); + if (btnval >= swpos) btnval = swpos - 1; + } + } + else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_SW_EMU) { + btnval = (value > 1024); + } + else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { + btnval = (value >> 6); + } + else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) { + if (swpos == 1 || swpos == 2) { + btnval = (value > 1024); + } else if (swpos == 3) { + btnval = (value < 1024) ? 1 : (value > 1024) ? 2 : 0; + } else { + btnval = static_cast(value) / (2048 / swpos); + if (btnval >= swpos) btnval = swpos - 1; + } + } + + // Channel Output value changed + if ((_usbLastChannelOutput[chix] == 0xffff) || (_usbLastChannelOutput[chix] != btnval)) { + + if ((_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_NORMAL) + || (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE)) { + + if (swpos == 1) { + setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 1); + } else { + setBtnBits(_usbJoystickCh[chix].btn_num, 1 << btnval, swpos); + } + + // Timer + if(_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) { + _usbChannelTimerActive[chix] = 1; + _usbChannelTimer[chix] = g_usbTmr10ms; + } + } + else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_SW_EMU) { + if ((_usbLastChannelOutput[chix] != 0xffff) && (_usbLastChannelOutput[chix] < btnval)) { + toggleBtnBit(_usbJoystickCh[chix].btn_num); + } + } + else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { + if (_usbLastChannelOutput[chix] != 0xffff) { + if (_usbLastChannelOutput[chix] < btnval) { + setBtnBits(_usbJoystickCh[chix].btn_num, 2, 2); + } + else if (_usbLastChannelOutput[chix] > btnval) { + setBtnBits(_usbJoystickCh[chix].btn_num, 1, 2); + } + _usbChannelTimerActive[chix] = 1; + _usbChannelTimer[chix] = g_usbTmr10ms; + } + } + else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) { + if (swpos == 1 || swpos == 2) { + setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 1); + } else if (swpos == 3) { + setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 2); + } else { + setBtnBits(_usbJoystickCh[chix].btn_num, 1 << btnval, swpos); + } + } + + _usbLastChannelOutput[chix] = btnval; + } + + // Timer check + if((_usbChannelTimerActive[chix]) && (((uint8_t)(g_usbTmr10ms - _usbChannelTimer[chix]) >= BTNPUSH_TIME))) { + _usbChannelTimerActive[chix] = 0; + + if(_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) { + setBtnBits(_usbJoystickCh[chix].btn_num, 0, swpos); + } + else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { + setBtnBits(_usbJoystickCh[chix].btn_num, 0, 2); + } + } + } + + memcpy(_hidReport + button_ix, _buttonState, (USBJ_BUTTON_SIZE >> 3)); + + for (uint8_t i = 0; i < _usbJoystickAxisCount; i++) { + chix = _usbJoystickAxisChannels[i]; + + value = circularCutoutValue(chix) + 1024; + if ( value > 2047 ) value = 2047; + else if ( value < 0 ) value = 0; + + if (_usbJoystickCh[chix].inversion) value = 2047 - value; + + _hidReport[i*2 + axis_ix] = static_cast(value & 0xFF); + _hidReport[i*2 + axis_ix+1] = static_cast((value >> 8) & 0x07); + } +} + +uint8_t usbReportSize() +{ + return _hidReportSize; +} + +struct usbReport_t usbReport() +{ + if (!usbJoystickExtMode()) { + // Classic USB Joystick report + usbClassicStateUpdate(); + } else { + usbStateUpdate(); + } + + usbReport_t res = { _hidReport, _hidReportSize }; + return res; +} + +void onUSBJoystickModelChanged() +{ + if (!usbJoystickActive()) return; + + if (setupUSBJoystick()) { +#if defined(STM32) && !defined(SIMU) + usbJoystickRestart(); +#endif + } +} + +int isUSBAxisCollision(uint8_t chIdx) +{ + USBJoystickChData * cch = usbJChAddress(chIdx); + + // non axis + if (cch->mode != USBJOYS_CH_AXIS) return false; + + // non-unique axis check + if (_usbJoystickUniqueAxis[cch->param] == 0) { + return false; + } + + // collision check + for (int i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) { + if (i == chIdx) continue; + + USBJoystickChData * och = usbJChAddress(i); + + if (cch->mode != och->mode) continue; + if (cch->param == och->param) { + return true; + } + } + + return false; +} + +int isUSBSimCollision(uint8_t chIdx) +{ + USBJoystickChData * cch = usbJChAddress(chIdx); + + // non axis + if (cch->mode != USBJOYS_CH_SIM) return false; + + // non-unique axis check + if (_usbJoystickUniqueSims[cch->param] == 0) { + return false; + } + + // collision check + for (int i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) { + if (i == chIdx) continue; + + USBJoystickChData * och = usbJChAddress(i); + + if (cch->mode != och->mode) continue; + if (cch->param == och->param) { + return true; + } + } + + return false; +} + +int isUSBBtnNumCollision(uint8_t chIdx) +{ + USBJoystickChData * cch = usbJChAddress(chIdx); + + // non button + if (cch->mode != USBJOYS_CH_BUTTON) return false; + + // collision check + uint8_t cfirst = cch->btn_num; + uint8_t clast = cch->lastBtnNum(); + for (int i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) { + if (i == chIdx) continue; + + USBJoystickChData * och = usbJChAddress(i); + + if (och->mode != USBJOYS_CH_BUTTON) continue; + + uint8_t ofirst = och->btn_num; + uint8_t olast = och->lastBtnNum(); + + // overlap + if ((cfirst <= olast) && (ofirst <= clast)) return true; + } + + return false; +} diff --git a/radio/src/usb_joystick.h b/radio/src/usb_joystick.h new file mode 100644 index 000000000..b53264346 --- /dev/null +++ b/radio/src/usb_joystick.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 for more details. + */ + +#ifndef _USB_JOYSTICK_H_ +#define _USB_JOYSTICK_H_ + +#include "storage/yaml/yaml_defs.h" + +enum USBJoystickIfMode +{ + USBJOYS_JOYSTICK, + USBJOYS_GAMEPAD, + USBJOYS_MULTIAXIS, + USBJOYS_LAST SKIP = USBJOYS_MULTIAXIS +}; + +enum USBJoystickCh +{ + USBJOYS_CH_NONE, + USBJOYS_CH_BUTTON, + USBJOYS_CH_AXIS, + USBJOYS_CH_SIM, + USBJOYS_CH_LAST SKIP = USBJOYS_CH_SIM +}; + +enum USBJoystickBtnMode +{ + USBJOYS_BTN_MODE_NORMAL, + USBJOYS_BTN_MODE_ON_PULSE, + USBJOYS_BTN_MODE_SW_EMU, + USBJOYS_BTN_MODE_DELTA, + USBJOYS_BTN_MODE_COMPANION, + USBJOYS_BTN_MODE_LAST SKIP = USBJOYS_BTN_MODE_COMPANION +}; + +enum USBJoystickAxis +{ + USBJOYS_AXIS_X, + USBJOYS_AXIS_Y, + USBJOYS_AXIS_Z, + USBJOYS_AXIS_RX, + USBJOYS_AXIS_RY, + USBJOYS_AXIS_RZ, + USBJOYS_AXIS_SLIDER, + USBJOYS_AXIS_DIAL, + USBJOYS_AXIS_WHEEL, + USBJOYS_AXIS_LAST SKIP = USBJOYS_AXIS_WHEEL +}; + +enum USBJoystickSim +{ + USBJOYS_SIM_AILERON, + USBJOYS_SIM_ELEVATOR, + USBJOYS_SIM_RUDDER, + USBJOYS_SIM_THROTTLE, + USBJOYS_SIM_LAST SKIP = USBJOYS_SIM_THROTTLE +}; + +#define USBJ_BUTTON_SIZE 32 +#define USBJ_MAX_JOYSTICK_CHANNELS 26 + +#ifdef __cplusplus +extern "C" { +#endif + struct usbReport_t { + uint8_t * ptr; + uint8_t size; + }; +#ifdef __cplusplus +} +#endif + +int usbJoystickExtMode(); +int usbJoystickActive(); +int usbJoystickSettingsChanged(); + +// Initializes the data based on the model settings +// Return value (true = description changed, false = desc. update not needed) +int setupUSBJoystick(); + +void onUSBJoystickModelChanged(); + +#ifdef __cplusplus +extern "C" { +#endif + struct usbReport_t usbReportDesc(); + uint8_t usbReportSize(); +#ifdef __cplusplus +} +#endif + +struct usbReport_t usbReport(); + +int isUSBAxisCollision(uint8_t chIdx); +int isUSBSimCollision(uint8_t chIdx); +int isUSBBtnNumCollision(uint8_t chIdx); + +#endif // _USB_JOYSTICK_H_