diff --git a/companion/src/apppreferencesdialog.cpp b/companion/src/apppreferencesdialog.cpp index c20001a6011..aca3a8dc5d7 100644 --- a/companion/src/apppreferencesdialog.cpp +++ b/companion/src/apppreferencesdialog.cpp @@ -97,12 +97,13 @@ void AppPreferencesDialog::accept() if (ui->joystickChkB ->isChecked()) { g.jsSupport(ui->joystickChkB ->isChecked()); // Don't overwrite selected joystick if not connected. Avoid surprising the user. - if (ui->joystickCB->isEnabled()) - g.jsCtrl(ui->joystickCB ->currentIndex()); + if (ui->joystickCB->isEnabled()) { + profile.jsName(ui->joystickCB->currentText()); + g.loadNamedJS(); + } } else { g.jsSupport(false); - g.jsCtrl(0); } // Updates tab @@ -287,7 +288,8 @@ void AppPreferencesDialog::initSettings() } ui->joystickCB->clear(); ui->joystickCB->insertItems(0, joystickNames); - ui->joystickCB->setCurrentIndex(g.jsCtrl()); + int stick = joystick->findCurrent(g.currentProfile().jsName()); + ui->joystickCB->setCurrentIndex(stick); } else { ui->joystickCB->clear(); @@ -614,8 +616,9 @@ void AppPreferencesDialog::on_joystickChkB_clicked() { } void AppPreferencesDialog::on_joystickcalButton_clicked() { - joystickDialog * jd=new joystickDialog(this, ui->joystickCB->currentIndex()); - jd->exec(); + g.currentProfile().jsName(ui->joystickCB->currentText()); + joystickDialog * jd = new joystickDialog(this); + jd->exec(); } #endif diff --git a/companion/src/simulation/joystick.cpp b/companion/src/simulation/joystick.cpp index 34f56726b25..b32991d3b08 100644 --- a/companion/src/simulation/joystick.cpp +++ b/companion/src/simulation/joystick.cpp @@ -167,3 +167,13 @@ int Joystick::getAxisValue(int axis) } else return 0; } + +int Joystick::findCurrent(QString jsName) +{ + for (int i = 0; i < joystickNames.size(); i += 1) { + if (joystickNames[i] == jsName) { + return i; + } + } + return 0; +} diff --git a/companion/src/simulation/joystick.h b/companion/src/simulation/joystick.h index 1c1ed3091e9..bfde75f38e8 100644 --- a/companion/src/simulation/joystick.h +++ b/companion/src/simulation/joystick.h @@ -62,6 +62,8 @@ class Joystick : public QObject } int getAxisValue(int); + int findCurrent(QString jsName); + private: QMap axes; QMap buttons; diff --git a/companion/src/simulation/joystickdialog.cpp b/companion/src/simulation/joystickdialog.cpp index a43e5b501ad..b7c004c413a 100644 --- a/companion/src/simulation/joystickdialog.cpp +++ b/companion/src/simulation/joystickdialog.cpp @@ -25,29 +25,70 @@ #include "boards.h" #include "constants.h" -joystickDialog::joystickDialog(QWidget *parent, int stick) : +joystickDialog::joystickDialog(QWidget *parent) : QDialog(parent), ui(new Ui::joystickDialog), step(0), numAxes(0), + numButtons(0), started(false) { ui->setupUi(this); int i; - char s[20]; for (i = 0; i < MAX_JS_AXES; i += 1) { jscal[i][0] = 32767; jscal[i][1] = 0; - jscal[i][2] = -32767; + jscal[i][2] = -32768; + } + + ui->backButton->setEnabled(false); + + ui->joystickChkB->setChecked(g.jsSupport()); + + if (loadJoysticks()) { + joystickSetEnabled(ui->joystickChkB->isChecked()); + joystickOpen(ui->joystickCB->currentIndex()); } + else { + joystickSetEnabled(false); + } + 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))); +} + +joystickDialog::~joystickDialog() +{ + delete ui; +} + +void joystickDialog::loadGrid() +{ + int i; + char s[20]; QGridLayout *grid = findChild("gridLayout"); + + QLayoutItem* item; + while ((item = grid->takeAt(0)) != NULL) + { + delete item->widget(); + delete item; + } + + memset(sliders, 0, sizeof(sliders)); + memset(sticks, 0, sizeof(sliders)); + memset(invert, 0, sizeof(sliders)); + int row = 0; int col = 0; if (grid) { - for (i = 0; i < MAX_JS_AXES; i += 1, row += 1) { + for (i = 0; i < numAxes; i += 1, row += 1) { col = (i & 1) * 4; sprintf(s, "Ch%d", i + 1); QLabel *l = new QLabel(s); @@ -55,18 +96,18 @@ joystickDialog::joystickDialog(QWidget *parent, int stick) : QSlider *s = new QSlider(Qt::Horizontal); s->setMinimum(-32767); s->setMaximum(32767); - sliders[row] = s; + sliders[i] = s; grid->addWidget(s, row/2, col+1, 1, 1); QCheckBox *c = new QCheckBox(""); - invert[row] = c; + invert[i] = c; grid->addWidget(c, row/2, col+2, 1, 1); QComboBox *d = new QComboBox(); populateSourceCombo(d); - sticks[row] = d; + sticks[i] = 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) { + for (i = 0; i < numButtons; i += 1, row += 1) { col = (i & 1) * 4; sprintf(s, "Btn%d", i + 1); QLabel *l = new QLabel(s); @@ -74,16 +115,16 @@ joystickDialog::joystickDialog(QWidget *parent, int stick) : QSlider *s = new QSlider(Qt::Horizontal); s->setMinimum(0); s->setMaximum(1); - sliders[row] = s; + sliders[i+numAxes] = s; grid->addWidget(s, row/2, col+1, 1, 1); QComboBox *d = new QComboBox(); populateButtonCombo(d); - sticks[row] = d; + sticks[i+numAxes] = d; grid->addWidget(d, row/2, col+3, 1, 1); } } - for (int i = 0; i < MAX_JS_AXES; ++i) { + for (int i = 0; i < numAxes; ++i) { if (g.joystick[i].existsOnDisk()) { jscal[i][0] = g.joystick[i].stick_min(); jscal[i][1] = g.joystick[i].stick_med(); @@ -95,37 +136,11 @@ joystickDialog::joystickDialog(QWidget *parent, int stick) : } } - for (int i = 0; i < MAX_JS_BUTTONS; ++i) { + for (int i = 0; i < numButtons; ++i) { if (g.jsButton[i].existsOnDisk()) { - sticks[i+MAX_JS_AXES]->setCurrentIndex(sticks[i+MAX_JS_AXES]->findData(g.jsButton[i].button_idx())); + sticks[i+numAxes]->setCurrentIndex(sticks[i+numAxes]->findData(g.jsButton[i].button_idx())); } } - - ui->backButton->setEnabled(false); - - ui->joystickChkB->setChecked(g.jsSupport() || stick > -1); - - if (stick < 0) - stick = g.jsCtrl(); - - if (loadJoysticks(stick)) { - joystickSetEnabled(ui->joystickChkB->isChecked()); - joystickOpen(ui->joystickCB->currentIndex()); - } - else { - joystickSetEnabled(false); - } - 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))); -} - -joystickDialog::~joystickDialog() -{ - delete ui; } void joystickDialog::populateSourceCombo(QComboBox * cb) @@ -194,16 +209,16 @@ void joystickDialog::populateButtonCombo(QComboBox * cb) 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); + 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); + cb->addItem(wname + " Down", (i + ttlSwitches) | JS_BUTTON_3POS_DN); + cb->addItem(wname + " Up", (i + ttlSwitches) | JS_BUTTON_3POS_UP); } } } -bool joystickDialog::loadJoysticks(int stick) +bool joystickDialog::loadJoysticks() { QStringList joystickNames; bool found = false; @@ -219,7 +234,8 @@ bool joystickDialog::loadJoysticks(int stick) joystick->close(); } ui->joystickCB->insertItems(0, joystickNames); - if (found && stick < joystickNames.size()) { + if (found) { + int stick = joystick->findCurrent(g.currentProfile().jsName()); ui->joystickCB->setCurrentIndex(stick); } else if (!found) { @@ -230,12 +246,16 @@ bool joystickDialog::loadJoysticks(int stick) void joystickDialog::joystickOpen(int stick) { + numAxes = 0; + numButtons = 0; + if (stick < 0) return; joystick = new Joystick(this, 1, false, 0); if (joystick && joystick->open(stick)) { numAxes = std::min(joystick->numAxes, MAX_JS_AXES); + numButtons = std::min(joystick->numButtons, MAX_JS_BUTTONS); for (int j=0; jsensitivities[j] = 0; joystick->deadzones[j] = 20; @@ -244,6 +264,9 @@ void joystickDialog::joystickOpen(int stick) else { QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Cannot open joystick.")); } + g.currentProfile().jsName(ui->joystickCB->currentText()); + g.loadNamedJS(); + loadGrid(); } void joystickDialog::joystickSetEnabled(bool enable) @@ -256,7 +279,7 @@ void joystickDialog::joystickSetEnabled(bool enable) void joystickDialog::onjoystickAxisValueChanged(int axis, int value) { - if (axis >= MAX_JS_AXES) + if (axis >= numAxes) return; if (started) { @@ -274,10 +297,10 @@ void joystickDialog::onjoystickAxisValueChanged(int axis, int value) void joystickDialog::onjoystickButtonValueChanged(int button, bool state) { - if (button >= MAX_JS_BUTTONS) + if (button >= numButtons) return; - sliders[button + MAX_JS_AXES]->setValue(state); + sliders[button + numAxes]->setValue(state); } void joystickDialog::loadStep() @@ -290,6 +313,10 @@ void joystickDialog::loadStep() break; case 1: started = true; + for (int i=0; i < numAxes; i++) { + jscal[i][0] = 0; + jscal[i][2] = 0; + } ui->howtoLabel->setText(tr("Move sticks and pots in every direction making full movement\nPress Next when finished")); ui->nextButton->setText(tr("Next")); ui->backButton->setDisabled(true); @@ -345,12 +372,12 @@ void joystickDialog::on_cancelButton_clicked() void joystickDialog::on_okButton_clicked() { g.jsSupport(ui->joystickChkB->isChecked()); - g.jsCtrl(ui->joystickCB->currentIndex()); + g.currentProfile().jsName(ui->joystickCB->currentText()); if (joystick) joystick->close(); - if (!g.jsSupport() || g.jsCtrl() < 0) { + if (!g.jsSupport()) { this->accept(); return; } @@ -362,7 +389,9 @@ void joystickDialog::on_okButton_clicked() return; } - for (int i = 0; i < MAX_JS_AXES; ++i) { + g.clearJSData(); + + for (int i = 0; i < numAxes; ++i) { auto stick = sticks[i]->currentData().toInt(); if (stick < 0) { g.joystick[i].stick_axe(-1); @@ -373,20 +402,22 @@ void joystickDialog::on_okButton_clicked() g.joystick[i].stick_med(jscal[i][1]); g.joystick[i].stick_min(jscal[i][0]); g.joystick[i].stick_inv(invert[i]->isChecked() ); - qDebug() << "joystick mapping " << sticks[i]->objectName() << "stick:" << i << "axe:" << stick; + qDebug() << "joystick mapping " << sticks[i]->currentText() << "stick:" << i << "axe:" << stick << jscal[i][0] << jscal[i][1] << jscal[i][2]; } } - for (int i = 0; i < MAX_JS_BUTTONS; ++i) { - auto btn = sticks[i+MAX_JS_AXES]->currentData().toInt(); + for (int i = 0; i < numButtons; ++i) { + auto btn = sticks[i+numAxes]->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; + qDebug() << "joystick button mapping " << sticks[i+numAxes]->currentText() << "button:" << i << "idx:" << btn; } } + g.saveNamedJS(); + this->accept(); } diff --git a/companion/src/simulation/joystickdialog.h b/companion/src/simulation/joystickdialog.h index 4e3b50e1837..0254b72a4bc 100644 --- a/companion/src/simulation/joystickdialog.h +++ b/companion/src/simulation/joystickdialog.h @@ -46,7 +46,7 @@ class joystickDialog : public QDialog Q_OBJECT public: - explicit joystickDialog(QWidget *parent = 0, int stick=-1); + explicit joystickDialog(QWidget *parent = 0); ~joystickDialog(); Joystick *joystick; @@ -58,12 +58,15 @@ class joystickDialog : public QDialog QSlider * sliders[MAX_JS_AXES + MAX_JS_BUTTONS]; int step; int numAxes; + int numButtons; bool started; + void loadGrid(); + private slots: void populateSourceCombo(QComboBox * cb); void populateButtonCombo(QComboBox * cb); - bool loadJoysticks(int stick = -1); + bool loadJoysticks(); void joystickOpen(int stick); void joystickSetEnabled(bool enable); void onjoystickAxisValueChanged(int axis, int value); diff --git a/companion/src/simulation/simulatorwidget.cpp b/companion/src/simulation/simulatorwidget.cpp index 92933f17aa1..bbdf635f995 100644 --- a/companion/src/simulation/simulatorwidget.cpp +++ b/companion/src/simulation/simulatorwidget.cpp @@ -670,13 +670,15 @@ void SimulatorWidget::setupJoysticks() #ifdef JOYSTICKS bool joysticksEnabled = false; - if (g.jsSupport() && g.jsCtrl() > -1) { + if (g.jsSupport()) { if (!joystick) 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 stick = joystick->findCurrent(g.currentProfile().jsName()); + + if (joystick && joystick->open(stick)) { int numAxes = std::min(joystick->numAxes, MAX_JS_AXES); for (int j=0; jsensitivities[j] = 0; @@ -860,8 +862,8 @@ void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value) { #ifdef JOYSTICKS static const int ttlSticks = 4; - static const int ttlKnobs = Boards::getCapability(m_board, Board::Pots); - static const int ttlFaders = Boards::getCapability(m_board, Board::Sliders); + const int ttlKnobs = Boards::getCapability(m_board, Board::Pots); + const int ttlFaders = Boards::getCapability(m_board, Board::Sliders); static const int valueRange = 1024; if (!joystick || axis >= MAX_JS_AXES) @@ -916,9 +918,6 @@ void SimulatorWidget::onjoystickButtonValueChanged(int button, bool state) 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) { diff --git a/companion/src/storage/appdata.cpp b/companion/src/storage/appdata.cpp index 066469fedaf..9408cec2876 100644 --- a/companion/src/storage/appdata.cpp +++ b/companion/src/storage/appdata.cpp @@ -266,6 +266,36 @@ bool JButtonData::existsOnDisk() return (m_settings.value(settingsPath() % button_idx_key(), -1).toInt() > -1); } +NamedJStickData::NamedJStickData() : CompStoreObj(), index(-1) +{ + CompStoreObj::addObjectMapping(propertyGroup(), this); +} + +bool NamedJStickData::existsOnDisk() +{ + return (m_settings.value(settingsPath() % stick_axe_key(), -1).toInt() > -1); +} + +NamedJButtonData::NamedJButtonData() : CompStoreObj(), index(-1) +{ + CompStoreObj::addObjectMapping(propertyGroup(), this); +} + +bool NamedJButtonData::existsOnDisk() +{ + return (m_settings.value(settingsPath() % button_idx_key(), -1).toInt() > -1); +} + +NamedJSData::NamedJSData() : CompStoreObj(), index(-1) +{ + CompStoreObj::addObjectMapping(propertyGroup(), this); +} + +bool NamedJSData::existsOnDisk() +{ + return (m_settings.value(settingsPath() % jsName(), -1).toInt() > -1); +} + // ** Profile class******************** @@ -411,6 +441,13 @@ AppData::AppData() : joystick[i].setIndex(i); for (int i = 0; i < MAX_JS_BUTTONS; i++) jsButton[i].setIndex(i); + for (int i = 0; i < MAX_NAMED_JOYSTICKS; i++) { + namedJS[i].setIndex(i); + for (int a = 0; a < MAX_JS_AXES; a += 1) + namedJS[i].joystick[a].setIndex(a, i); + for (int b = 0; b < MAX_JS_BUTTONS; b += 1) + namedJS[i].jsButton[b].setIndex(b, i); + } // Configure the updates for (int i = 0; i < MAX_COMPONENTS; i++) { @@ -421,6 +458,74 @@ AppData::AppData() : } } +void AppData::saveNamedJS(int i) +{ + namedJS[i].jsName(currentProfile().jsName()); + for (int a = 0; a < MAX_JS_AXES; a += 1) { + namedJS[i].joystick[a].stick_axe(joystick[a].stick_axe()); + namedJS[i].joystick[a].stick_max(joystick[a].stick_max()); + namedJS[i].joystick[a].stick_med(joystick[a].stick_med()); + namedJS[i].joystick[a].stick_min(joystick[a].stick_min()); + namedJS[i].joystick[a].stick_min(joystick[a].stick_min()); + } + for (int b = 0; b < MAX_JS_BUTTONS; b += 1) { + namedJS[i].jsButton[b].button_idx(jsButton[b].button_idx()); + } + namedJS[i].jsLastUsed(time(NULL)); +} + +void AppData::saveNamedJS() +{ + for (int i = 0; i < MAX_NAMED_JOYSTICKS; i += 1) { + if (namedJS[i].jsName() == currentProfile().jsName()) { + saveNamedJS(i); + return; + } + } + + for (int i = 0; i < MAX_NAMED_JOYSTICKS; i += 1) { + if (namedJS[i].jsName() == "") { + saveNamedJS(i); + return; + } + } + + unsigned int oldestTime = namedJS[0].jsLastUsed(); + int oldestN = 0; + for (int i = 1; i < MAX_NAMED_JOYSTICKS; i += 1) { + if (namedJS[i].jsLastUsed() < oldestTime) { + oldestTime = namedJS[i].jsLastUsed(); + oldestN = i; + } + } + saveNamedJS(oldestN); +} + +void AppData::loadNamedJS(int i) +{ + for (int a = 0; a < MAX_JS_AXES; a += 1) { + joystick[a].stick_axe(namedJS[i].joystick[a].stick_axe()); + joystick[a].stick_max(namedJS[i].joystick[a].stick_max()); + joystick[a].stick_med(namedJS[i].joystick[a].stick_med()); + joystick[a].stick_min(namedJS[i].joystick[a].stick_min()); + joystick[a].stick_min(namedJS[i].joystick[a].stick_min()); + } + for (int b = 0; b < MAX_JS_BUTTONS; b += 1) { + jsButton[b].button_idx(namedJS[i].jsButton[b].button_idx()); + } + namedJS[i].jsLastUsed(time(NULL)); +} + +void AppData::loadNamedJS() +{ + for (int i = 0; i < MAX_NAMED_JOYSTICKS; i += 1) { + if (namedJS[i].jsName() == currentProfile().jsName()) { + loadNamedJS(i); + return; + } + } +} + static QString fmtHex(quint32 num) { return QString::number(num, 16).toUpper(); @@ -452,7 +557,14 @@ void AppData::initAll() joystick[i].init(); for (int i = 0; i < MAX_JS_BUTTONS; i++) jsButton[i].init(); - // Initialize the updatess + for (int i = 0; i < MAX_NAMED_JOYSTICKS; i++) { + namedJS[i].init(); + for (int a = 0; a < MAX_JS_AXES; a += 1) + namedJS[i].joystick[a].init(); + for (int b = 0; b < MAX_JS_BUTTONS; b += 1) + namedJS[i].jsButton[b].init(); + } + // Initialize the updates for (int i = 0; i < MAX_COMPONENTS; i++) { component[i].init(); for (int j = 0; j < MAX_COMPONENT_ASSETS; j++) { @@ -471,6 +583,13 @@ void AppData::resetAllSettings() joystick[i].resetAll(); for (int i = 0; i < MAX_JS_BUTTONS; i++) jsButton[i].resetAll(); + for (int i = 0; i < MAX_NAMED_JOYSTICKS; i++) { + namedJS[i].resetAll(); + for (int a = 0; a < MAX_JS_AXES; a += 1) + namedJS[i].joystick[a].resetAll(); + for (int b = 0; b < MAX_JS_BUTTONS; b += 1) + namedJS[i].jsButton[b].resetAll(); + } for (int i = 0; i < MAX_COMPONENTS; i++) { component[i].resetAll(); for (int j = 0; j < MAX_COMPONENT_ASSETS; j++) { @@ -489,6 +608,13 @@ void AppData::storeAllSettings() joystick[i].storeAll(); for (int i = 0; i < MAX_JS_BUTTONS; i++) jsButton[i].storeAll(); + for (int i = 0; i < MAX_NAMED_JOYSTICKS; i++) { + namedJS[i].storeAll(); + for (int a = 0; a < MAX_JS_AXES; a += 1) + namedJS[i].joystick[a].storeAll(); + for (int b = 0; b < MAX_JS_BUTTONS; b += 1) + namedJS[i].jsButton[b].storeAll(); + } for (int i = 0; i < MAX_COMPONENTS; i++) { component[i].storeAll(); for (int j = 0; j < MAX_COMPONENT_ASSETS; j++) @@ -506,6 +632,7 @@ void AppData::sessionId(int index) if (index < 0 || index >= MAX_PROFILES || index == m_sessionId) return; m_sessionId = index; + loadNamedJS(); emit sessionIdChanged(index); emit currentProfileChanged(); } diff --git a/companion/src/storage/appdata.h b/companion/src/storage/appdata.h index 88e9a3da689..93163b5387a 100644 --- a/companion/src/storage/appdata.h +++ b/companion/src/storage/appdata.h @@ -63,6 +63,7 @@ #define MAX_JS_BUTTONS 32 #define MAX_COMPONENTS 10 #define MAX_COMPONENT_ASSETS 5 +#define MAX_NAMED_JOYSTICKS 10 // It important that these function names are consistent everywhere. #define PROP_FSIG_INIT_IMPL _init() @@ -331,12 +332,22 @@ class JStickData: public CompStoreObj public slots: bool existsOnDisk(); + public: + void clear() { + stick_axe(-1); + stick_med(-32768); + stick_max(32767); + stick_med(0); + stick_inv(0); + } + protected: explicit JStickData(); void setIndex(int idx) { index = idx; } inline QString propertyGroup() const override { return QStringLiteral("JsCalibration"); } inline QString settingsPath() const override { return QString("%1/%2/").arg(propertyGroup()).arg(index); } friend class AppData; + friend class NamedJSData; private: PROPERTY(int, stick_axe, -1) @@ -348,22 +359,100 @@ class JStickData: public CompStoreObj int index; }; +//! \brief JButtonData class stores properties related to each joystick button (button number). class JButtonData: public CompStoreObj { Q_OBJECT public slots: bool existsOnDisk(); + public: + void clear() { + button_idx(-1); + } + 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; + friend class NamedJSData; private: PROPERTY(int, button_idx, -1) - PROPERTY(int, button_inv, 0) + + int index; +}; + +//! \brief NamedJStickData class stores properties related to each joystick axis (calibration/assignment/direction). +class NamedJStickData: public CompStoreObj +{ + Q_OBJECT + public slots: + bool existsOnDisk(); + + protected: + explicit NamedJStickData(); + void setIndex(int idx, int nmIdx) { index = idx; namedIdx = nmIdx; } + inline QString propertyGroup() const override { return QStringLiteral("NamedJSData/%1").arg(namedIdx); } + inline QString settingsPath() const override { return QString("%1/JsCalibration/%2/").arg(propertyGroup()).arg(index); } + friend class AppData; + friend class NamedJSData; + + private: + PROPERTY(int, stick_axe, -1) + PROPERTY(int, stick_min, -32768) + PROPERTY(int, stick_med, 0) + PROPERTY(int, stick_max, 32767) + PROPERTY(int, stick_inv, 0) + + int namedIdx; + int index; +}; + +//! \brief NamedJButtonData class stores properties related to each joystick button (button number). +class NamedJButtonData: public CompStoreObj +{ + Q_OBJECT + public slots: + bool existsOnDisk(); + + protected: + explicit NamedJButtonData(); + void setIndex(int idx, int nmIdx) { index = idx; namedIdx = nmIdx; } + inline QString propertyGroup() const override { return QStringLiteral("NamedJSData/%1").arg(namedIdx); } + inline QString settingsPath() const override { return QString("%1/JsButton/%2/").arg(propertyGroup()).arg(index); } + friend class AppData; + friend class NamedJSData; + + private: + PROPERTY(int, button_idx, -1) + + int namedIdx; + int index; +}; + +class NamedJSData: public CompStoreObj +{ + Q_OBJECT + public slots: + bool existsOnDisk(); + + protected: + explicit NamedJSData(); + void setIndex(int idx) { index = idx; } + inline QString propertyGroup() const override { return QStringLiteral("NamedJSData"); } + inline QString settingsPath() const override { return QString("%1/%2/").arg(propertyGroup()).arg(index); } + friend class AppData; + + public: + NamedJStickData joystick[MAX_JS_AXES]; + NamedJButtonData jsButton[MAX_JS_BUTTONS]; + + private: + PROPERTYSTRD(jsName, "") + PROPERTY(unsigned int, jsLastUsed, 0) int index; }; @@ -432,6 +521,8 @@ class Profile: public CompStoreObj PROPERTY4(int, txCurrentCalibration, "currentCalib", 0) PROPERTY4(int, txVoltageCalibration, "VbatCalib", 0) + PROPERTYSTRD(jsName, "") + int index; static const QStringList fwVarsList() //! for resetFwVariables()... TODO: make this go away @@ -615,9 +706,19 @@ class AppData: public CompStoreObj Profile profile[MAX_PROFILES]; JStickData joystick[MAX_JS_AXES]; JButtonData jsButton[MAX_JS_BUTTONS]; + NamedJSData namedJS[MAX_NAMED_JOYSTICKS]; FwRevision fwRev; ComponentData component[MAX_COMPONENTS]; + void clearJSData() { + for (int i = 0; i < MAX_JS_AXES; i += 1) + joystick[i].clear(); + for (int i = 0; i < MAX_JS_BUTTONS; i += 1) + jsButton[i].clear(); + } + void saveNamedJS(); + void loadNamedJS(); + signals: void currentProfileChanged(); @@ -625,6 +726,9 @@ class AppData: public CompStoreObj inline QString propertyGroup() const override { return QStringLiteral("General"); } inline QString settingsPath() const override { return QString(); } + void saveNamedJS(int i); + void loadNamedJS(int i); + private: PROPERTY_META(int, sessionId, 0) // currently active radio profile ID, NOT saved to persistent storage -- Initialize before profileId! @@ -679,7 +783,6 @@ class AppData: public CompStoreObj PROPERTY4(int, embedSplashes, "embedded_splashes", 0) PROPERTY4(int, fwServerFails, "fwserver", 0) PROPERTY4(int, iconSize, "icon_size", 2) - PROPERTY4(int, jsCtrl, "js_ctrl", 0) PROPERTY4(int, historySize, "history_size", 10) PROPERTY (int, generalEditTab, 0) PROPERTY (int, theme, 1) diff --git a/radio/src/datastructs_private.h b/radio/src/datastructs_private.h index 7422b51617c..001e93817e0 100644 --- a/radio/src/datastructs_private.h +++ b/radio/src/datastructs_private.h @@ -638,6 +638,12 @@ PACK(struct USBJoystickChData { #if defined(USBJ_EX) NOBACKUP( + uint8_t btnCount() { + // 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)) + return switch_npos; + return switch_npos + 1; + } uint8_t lastBtnNumNoCLip() { uint8_t last = btn_num + switch_npos; // Use one less joystick button for 2POS and 3POS switches for Companion mode diff --git a/radio/src/serial.cpp b/radio/src/serial.cpp index 2899ba9fba4..303deb60379 100644 --- a/radio/src/serial.cpp +++ b/radio/src/serial.cpp @@ -29,7 +29,7 @@ #include "hal/serial_port.h" -#if defined(CONFIGURABLE_MODULE_PORT) +#if defined(CONFIGURABLE_MODULE_PORT) and !defined(BOOT) #include "hal/module_port.h" #include "tasks/mixer_task.h" #endif @@ -256,7 +256,7 @@ static void serialSetCallBacks(int mode, void* ctx, const etx_serial_port_t* por break; #endif -#if defined(CONFIGURABLE_MODULE_PORT) +#if defined(CONFIGURABLE_MODULE_PORT) and !defined(BOOT) case UART_MODE_EXT_MODULE: if (port && !ctx) { // de-init etx_module_port_t mod_port; @@ -400,7 +400,7 @@ void serialInit(uint8_t port_nr, int mode) memset(state, 0, sizeof(SerialPortState)); } -#if defined(CONFIGURABLE_MODULE_PORT) +#if defined(CONFIGURABLE_MODULE_PORT) and !defined(BOOT) if (mode == UART_MODE_EXT_MODULE) { etx_module_port_t mod_port = { .port = ETX_MOD_PORT_UART, diff --git a/radio/src/usb_joystick.cpp b/radio/src/usb_joystick.cpp index 047d3009fbb..07f37e3147e 100644 --- a/radio/src/usb_joystick.cpp +++ b/radio/src/usb_joystick.cpp @@ -25,43 +25,40 @@ #define MAX_HID_REPORTDESC 160 #define MAX_HID_REPORT 80 -uint8_t _hidReportDesc[MAX_HID_REPORTDESC] = { }; +uint8_t* _hidReportDesc = nullptr; uint8_t _hidReportDescSize = 0; -uint8_t _hidReport[MAX_HID_REPORT] = { }; +uint8_t* _hidReport = nullptr; uint8_t _hidReportSize = 0; -static uint8_t _buttonState[USBJ_BUTTON_SIZE >> 3] = { }; +struct _usbJSData { + uint16_t _usbLastChannelOutput[USBJ_MAX_JOYSTICK_CHANNELS]; + uint8_t _usbChannelTimerActive[USBJ_MAX_JOYSTICK_CHANNELS]; + uint8_t _usbChannelTimer[USBJ_MAX_JOYSTICK_CHANNELS]; + uint8_t _usbJoystickButtonChannels[USBJ_MAX_JOYSTICK_CHANNELS]; + uint8_t _usbJoystickAxisChannels[USBJ_MAX_JOYSTICK_CHANNELS]; +}; + +static struct _usbJSData* _usbJS = nullptr; -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 _buttonState[(USBJ_BUTTON_SIZE+7) >> 3] = { }; 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: + Report 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 @@ -99,7 +96,7 @@ static const uint8_t HID_JOYSTICK_ReportDesc[] = 0x09, 0x34, // USAGE (Ry) 0x09, 0x35, // USAGE (Rz) 0x09, 0x36, // USAGE (Slider) - 0x09, 0x36, // USAGE (Slider) + 0x09, 0x37, // USAGE (Dial) 0x16, 0x00, 0x00, // LOGICAL_MINIMUM (0) 0x26, 0xFF, 0x07, // LOGICAL_MAXIMUM (2047) 0x75, 0x10, // REPORT_SIZE (16) @@ -109,6 +106,26 @@ static const uint8_t HID_JOYSTICK_ReportDesc[] = 0xc0 // END_COLLECTION }; +static bool isUniqueAxisType(int type) { return type < 6; } +static bool isUniqueSimType(int type) { return true; } + +const uint32_t MOD_ADLER = 65521; + +uint32_t adler32(uint8_t *data, size_t len) +{ + uint32_t a = 1, b = 0; + size_t index; + + // Process each byte of the data in order + for (index = 0; index < len; ++index) + { + a = (a + data[index]) % MOD_ADLER; + b = (b + a) % MOD_ADLER; + } + + return (b << 16) | a; +} + int usbJoystickExtMode() { return g_model.usbJoystickExtMode; @@ -121,13 +138,19 @@ int usbJoystickActive() int usbJoystickSettingsChanged() { - if (!usbJoystickActive()) return false; + static uint32_t settingsChecksum = 0; - if((_usbJoystickIfMode != g_model.usbJoystickIfMode) - || (_usbJoystickCircularCut != g_model.usbJoystickCircularCut)) return true; - if(memcmp(_usbJoystickCh, g_model.usbJoystickCh, sizeof(g_model.usbJoystickCh)) != 0) return true; + if (!usbJoystickActive()) + return false; - return false; + if ((_usbJoystickIfMode != g_model.usbJoystickIfMode) || + (_usbJoystickCircularCut != g_model.usbJoystickCircularCut)) + return true; + + uint32_t oldChecksum = settingsChecksum; + settingsChecksum = adler32((uint8_t*)&g_model.usbJoystickCh, sizeof(g_model.usbJoystickCh)); + + return oldChecksum != settingsChecksum; } int setupUSBJoystick() @@ -137,31 +160,42 @@ int setupUSBJoystick() 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; + if (_hidReportDesc == nullptr) { + _hidReportDesc = (uint8_t*)malloc(MAX_HID_REPORTDESC); + if (_hidReportDesc == nullptr) + return false; + } + + if (_hidReport == nullptr) { + _hidReport = (uint8_t*)malloc(MAX_HID_REPORT); + if (_hidReport == nullptr) + return false; + } + + if (_usbJS == nullptr) { + _usbJS = (struct _usbJSData*)malloc(sizeof(struct _usbJSData)); + if (_usbJS == nullptr) + return false; + } - // copy old to compare - memcpy(oldHIDReportDesc, _hidReportDesc, MAX_HID_REPORTDESC); - oldHIDReportDescSize = _hidReportDescSize; + // properties to detect change + uint8_t oldHIDReportDescSize = _hidReportDescSize; + uint32_t oldChecksum = adler32(_hidReportDesc, _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(_buttonState, 0, ((USBJ_BUTTON_SIZE+7) >> 3)); + memset(_usbJS, 0, sizeof(struct _usbJSData)); + memset(_usbJS->_usbLastChannelOutput, 0xff, sizeof(_usbJS->_usbLastChannelOutput)); - memset(_usbJoystickButtonChannels, 0, USBJ_MAX_JOYSTICK_CHANNELS); _usbJoystickButtonCount = 0; - memset(_usbJoystickAxisChannels, 0, USBJ_MAX_JOYSTICK_CHANNELS); _usbJoystickAxisCount = 0; memset(_usbJoystickAxisPairs, 0xff, sizeof(_usbJoystickAxisPairs)); @@ -175,6 +209,7 @@ int setupUSBJoystick() uint8_t genAxisCount = 0; uint8_t simAxisCount = 0; + uint8_t buttonCount = 0; // sort channels by type uint8_t typeCount[USBJOYS_CH_LAST + 1] = { }; @@ -185,24 +220,25 @@ int setupUSBJoystick() uint8_t mode = 0; uint8_t submode = 0; for (uint8_t i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) { - mode = _usbJoystickCh[i].mode; + mode = g_model.usbJoystickCh[i].mode; - if (_usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) { + if (g_model.usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) { + buttonCount += g_model.usbJoystickCh[i].btnCount(); typeCount[mode]++; } - else if (_usbJoystickCh[i].mode == USBJOYS_CH_AXIS) { - submode = _usbJoystickCh[i].param; + else if (g_model.usbJoystickCh[i].mode == USBJOYS_CH_AXIS) { + submode = g_model.usbJoystickCh[i].param; if (submode <= USBJOYS_AXIS_LAST) { - if ((_usbJoystickUniqueAxis[submode] == 0) || (axisCount[submode] == 0)) { + if (!isUniqueAxisType(submode) || (axisCount[submode] == 0)) { axisCount[submode]++; typeCount[mode]++; } } } - else if (_usbJoystickCh[i].mode == USBJOYS_CH_SIM) { - submode = _usbJoystickCh[i].param; + else if (g_model.usbJoystickCh[i].mode == USBJOYS_CH_SIM) { + submode = g_model.usbJoystickCh[i].param; if (submode <= USBJOYS_SIM_LAST) { - if ((_usbJoystickUniqueSims[submode] == 0) || (simCount[submode] == 0)) { + if (!isUniqueSimType(submode) || (simCount[submode] == 0)) { simCount[submode]++; typeCount[mode]++; } @@ -221,17 +257,17 @@ int setupUSBJoystick() } ixcount = 0; for (uint8_t i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) { - mode = _usbJoystickCh[i].mode; + mode = g_model.usbJoystickCh[i].mode; - if (_usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) { - _usbJoystickButtonChannels[ixcount] = i; + if (g_model.usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) { + _usbJS->_usbJoystickButtonChannels[ixcount] = i; ixcount++; } - else if (_usbJoystickCh[i].mode == USBJOYS_CH_AXIS) { - submode = _usbJoystickCh[i].param; + else if (g_model.usbJoystickCh[i].mode == USBJOYS_CH_AXIS) { + submode = g_model.usbJoystickCh[i].param; if (submode <= USBJOYS_AXIS_LAST) { - _usbJoystickAxisChannels[axisIx[submode]] = i; - if (_usbJoystickUniqueAxis[submode] == 0) { + _usbJS->_usbJoystickAxisChannels[axisIx[submode]] = i; + if (!isUniqueAxisType(submode)) { axisIx[submode]++; } // save axis pairs @@ -253,11 +289,11 @@ int setupUSBJoystick() } } } - else if (_usbJoystickCh[i].mode == USBJOYS_CH_SIM) { - submode = _usbJoystickCh[i].param; + else if (g_model.usbJoystickCh[i].mode == USBJOYS_CH_SIM) { + submode = g_model.usbJoystickCh[i].param; if (submode <= USBJOYS_SIM_LAST) { - _usbJoystickAxisChannels[simIx[submode]] = i; - if (_usbJoystickUniqueSims[submode] == 0) { + _usbJS->_usbJoystickAxisChannels[simIx[submode]] = i; + if (!isUniqueSimType(submode)) { simIx[submode]++; } } @@ -273,61 +309,17 @@ int setupUSBJoystick() // generate report desc // USAGE_PAGE (Generic Desktop) - _hidReportDesc[_hidReportDescSize++] = 0x05; - _hidReportDesc[_hidReportDescSize++] = 0x01; + memcpy(_hidReportDesc, HID_JOYSTICK_ReportDesc, 24); + _hidReportDescSize = 24; // 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; + _hidReportDesc[3] = joystickType; + _hidReportDesc[13] = buttonCount ? buttonCount : 1; + _hidReportDesc[19] = USBJ_BUTTON_SIZE; // generic axis types if (genAxisCount > 0) { @@ -344,27 +336,9 @@ int setupUSBJoystick() } } - // 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; + memcpy(_hidReportDesc+_hidReportDescSize, HID_JOYSTICK_ReportDesc+42, 12); + _hidReportDesc[_hidReportDescSize+9] = genAxisCount; + _hidReportDescSize += 12; } // sim axis types @@ -382,27 +356,9 @@ int setupUSBJoystick() } } - // 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; + memcpy(_hidReportDesc+_hidReportDescSize, HID_JOYSTICK_ReportDesc+42, 12); + _hidReportDesc[_hidReportDescSize+9] = simAxisCount; + _hidReportDescSize += 12; } // END_COLLECTION @@ -413,14 +369,13 @@ int setupUSBJoystick() // end of report desc - _hidReportSize = (USBJ_BUTTON_SIZE / 8) + (_usbJoystickAxisCount * 2); + _hidReportSize = ((USBJ_BUTTON_SIZE+7) / 8) + (_usbJoystickAxisCount * 2); } - + //compare with the old description if (_hidReportDescSize != oldHIDReportDescSize) return true; - if (memcmp(_hidReportDesc, oldHIDReportDesc, _hidReportDescSize) != 0) return true; - - return false; + uint32_t newChecksum = adler32(_hidReportDesc, _hidReportDescSize); + return oldChecksum != newChecksum; } extern "C" struct usbReport_t usbReportDesc() @@ -431,6 +386,8 @@ extern "C" struct usbReport_t usbReportDesc() void usbClassicStateUpdate() { + if (_hidReport == nullptr) return; + memset(_hidReport, 0, MAX_HID_REPORT); //buttons @@ -519,8 +476,10 @@ static int16_t circularCutoutValue(uint8_t chix) void usbStateUpdate() { + if (_hidReport == nullptr) return; + const uint8_t button_ix = 0; - const uint8_t axis_ix = button_ix + (USBJ_BUTTON_SIZE >> 3); + const uint8_t axis_ix = button_ix + ((USBJ_BUTTON_SIZE+7) >> 3); memset(_hidReport, 0, MAX_HID_REPORT); @@ -532,19 +491,19 @@ void usbStateUpdate() uint8_t btnval = 0; for (uint8_t i = 0; i < _usbJoystickButtonCount; i++) { - chix = _usbJoystickButtonChannels[i]; + chix = _usbJS->_usbJoystickButtonChannels[i]; value = channelOutputs[chix] + 1024; if ( value > 2047 ) value = 2047; else if ( value < 0 ) value = 0; - if (_usbJoystickCh[chix].inversion) value = 2047 - value; + if (g_model.usbJoystickCh[chix].inversion) value = 2047 - value; - swpos = _usbJoystickCh[chix].switch_npos + 1; + swpos = g_model.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 ((g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_NORMAL) + || (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE)) { if (swpos == 1) { btnval = (value > 1024); @@ -553,13 +512,13 @@ void usbStateUpdate() if (btnval >= swpos) btnval = swpos - 1; } } - else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_SW_EMU) { + else if (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_SW_EMU) { btnval = (value > 1024); } - else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { + else if (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { btnval = (value >> 6); } - else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) { + else if (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) { if (swpos == 1 || swpos == 2) { btnval = (value > 1024); } else if (swpos == 3) { @@ -571,76 +530,76 @@ void usbStateUpdate() } // Channel Output value changed - if ((_usbLastChannelOutput[chix] == 0xffff) || (_usbLastChannelOutput[chix] != btnval)) { + if ((_usbJS->_usbLastChannelOutput[chix] == 0xffff) || (_usbJS->_usbLastChannelOutput[chix] != btnval)) { - if ((_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_NORMAL) - || (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE)) { + if ((g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_NORMAL) + || (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE)) { if (swpos == 1) { - setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 1); + setBtnBits(g_model.usbJoystickCh[chix].btn_num, btnval, 1); } else { - setBtnBits(_usbJoystickCh[chix].btn_num, 1 << btnval, swpos); + setBtnBits(g_model.usbJoystickCh[chix].btn_num, 1 << btnval, swpos); } // Timer - if(_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) { - _usbChannelTimerActive[chix] = 1; - _usbChannelTimer[chix] = g_usbTmr10ms; + if(g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) { + _usbJS->_usbChannelTimerActive[chix] = 1; + _usbJS->_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 (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_SW_EMU) { + if ((_usbJS->_usbLastChannelOutput[chix] != 0xffff) && (_usbJS->_usbLastChannelOutput[chix] < btnval)) { + toggleBtnBit(g_model.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 (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { + if (_usbJS->_usbLastChannelOutput[chix] != 0xffff) { + if (_usbJS->_usbLastChannelOutput[chix] < btnval) { + setBtnBits(g_model.usbJoystickCh[chix].btn_num, 2, 2); } - else if (_usbLastChannelOutput[chix] > btnval) { - setBtnBits(_usbJoystickCh[chix].btn_num, 1, 2); + else if (_usbJS->_usbLastChannelOutput[chix] > btnval) { + setBtnBits(g_model.usbJoystickCh[chix].btn_num, 1, 2); } - _usbChannelTimerActive[chix] = 1; - _usbChannelTimer[chix] = g_usbTmr10ms; + _usbJS->_usbChannelTimerActive[chix] = 1; + _usbJS->_usbChannelTimer[chix] = g_usbTmr10ms; } } - else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) { + else if (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) { if (swpos == 1 || swpos == 2) { - setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 1); + setBtnBits(g_model.usbJoystickCh[chix].btn_num, btnval, 1); } else if (swpos == 3) { - setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 2); + setBtnBits(g_model.usbJoystickCh[chix].btn_num, btnval, 2); } else { - setBtnBits(_usbJoystickCh[chix].btn_num, 1 << btnval, swpos); + setBtnBits(g_model.usbJoystickCh[chix].btn_num, 1 << btnval, swpos); } } - _usbLastChannelOutput[chix] = btnval; + _usbJS->_usbLastChannelOutput[chix] = btnval; } // Timer check - if((_usbChannelTimerActive[chix]) && (((uint8_t)(g_usbTmr10ms - _usbChannelTimer[chix]) >= BTNPUSH_TIME))) { - _usbChannelTimerActive[chix] = 0; + if((_usbJS->_usbChannelTimerActive[chix]) && (((uint8_t)(g_usbTmr10ms - _usbJS->_usbChannelTimer[chix]) >= BTNPUSH_TIME))) { + _usbJS->_usbChannelTimerActive[chix] = 0; - if(_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) { - setBtnBits(_usbJoystickCh[chix].btn_num, 0, swpos); + if(g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) { + setBtnBits(g_model.usbJoystickCh[chix].btn_num, 0, swpos); } - else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { - setBtnBits(_usbJoystickCh[chix].btn_num, 0, 2); + else if (g_model.usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) { + setBtnBits(g_model.usbJoystickCh[chix].btn_num, 0, 2); } } } - - memcpy(_hidReport + button_ix, _buttonState, (USBJ_BUTTON_SIZE >> 3)); + + memcpy(_hidReport + button_ix, _buttonState, ((USBJ_BUTTON_SIZE+7) >> 3)); for (uint8_t i = 0; i < _usbJoystickAxisCount; i++) { - chix = _usbJoystickAxisChannels[i]; + chix = _usbJS->_usbJoystickAxisChannels[i]; value = circularCutoutValue(chix) + 1024; if ( value > 2047 ) value = 2047; else if ( value < 0 ) value = 0; - if (_usbJoystickCh[chix].inversion) value = 2047 - value; + if (g_model.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); @@ -684,7 +643,7 @@ int isUSBAxisCollision(uint8_t chIdx) if (cch->mode != USBJOYS_CH_AXIS) return false; // non-unique axis check - if (_usbJoystickUniqueAxis[cch->param] == 0) { + if (!isUniqueAxisType(cch->param)) { return false; } @@ -711,7 +670,7 @@ int isUSBSimCollision(uint8_t chIdx) if (cch->mode != USBJOYS_CH_SIM) return false; // non-unique axis check - if (_usbJoystickUniqueSims[cch->param] == 0) { + if (!isUniqueSimType(cch->param)) { return false; }