Bsongis/issue 4400 (#4425)
[opentx.git] / companion / src / simulation / simulatorwidget.cpp
blob6eb9eadfdf9419772553a1f229d3d4c9af8d1be7
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "simulatorwidget.h"
22 #include "ui_simulatorwidget.h"
24 #include "appdata.h"
25 #include "radiofaderwidget.h"
26 #include "radioknobwidget.h"
27 #include "radioswitchwidget.h"
28 #include "radiouiaction.h"
29 #include "sdcard.h"
30 #include "simulateduiwidget.h"
31 #include "simulatorinterface.h"
32 #include "storage.h"
33 #include "virtualjoystickwidget.h"
34 #ifdef JOYSTICKS
35 #include "joystick.h"
36 #include "joystickdialog.h"
37 #endif
39 #include <QDebug>
40 #include <QFile>
41 #include <iostream>
43 SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator, quint8 flags):
44 QWidget(parent),
45 ui(new Ui::SimulatorWidget),
46 simulator(simulator),
47 firmware(getCurrentFirmware()),
48 radioSettings(GeneralSettings()),
49 timer(NULL),
50 radioUiWidget(NULL),
51 vJoyLeft(NULL),
52 vJoyRight(NULL),
53 m_board(getCurrentBoard()),
54 flags(flags),
55 lastPhase(-1),
56 buttonPressed(0),
57 trimPressed(TRIM_NONE),
58 startupFromFile(false),
59 deleteTempRadioData(false),
60 saveTempRadioData(false),
61 middleButtonPressed(false),
62 firstShow(true)
65 #ifdef JOYSTICKS
66 joystick = NULL;
67 #endif
69 // defaults
70 setRadioProfileId(g.sessionId());
71 setSdPath(g.profile[radioProfileId].sdPath());
73 ui->setupUi(this);
75 windowName = tr("Radio Simulator (%1)").arg(firmware->getName());
76 setWindowTitle(windowName);
78 switch(m_board) {
79 case Board::BOARD_TARANIS_X7 :
80 radioUiWidget = new SimulatedUIWidgetX7(simulator, this);
81 break;
82 case Board::BOARD_TARANIS_X9D :
83 case Board::BOARD_TARANIS_X9DP :
84 radioUiWidget = new SimulatedUIWidgetX9(simulator, this);
85 break;
86 case Board::BOARD_TARANIS_X9E :
87 radioUiWidget = new SimulatedUIWidgetX9E(simulator, this);
88 break;
89 case Board::BOARD_X12S :
90 case Board::BOARD_X10 :
91 radioUiWidget = new SimulatedUIWidgetX12(simulator, this);
92 break;
93 default:
94 radioUiWidget = new SimulatedUIWidget9X(simulator, this);
95 break;
98 foreach (keymapHelp_t item, *radioUiWidget->getKeymapHelp())
99 keymapHelp.append(item);
101 ui->radioUiWidget->layout()->removeItem(ui->radioUiTempSpacer);
102 ui->radioUiWidget->layout()->addWidget(radioUiWidget);
103 radioUiWidget->setFocusPolicy(Qt::WheelFocus);
104 radioUiWidget->setFocus();
106 connect(radioUiWidget, &SimulatedUIWidget::customStyleRequest, this, &SimulatorWidget::setUiAreaStyle);
108 vJoyLeft = new VirtualJoystickWidget(this, 'L');
109 ui->leftStickLayout->addWidget(vJoyLeft);
111 vJoyRight = new VirtualJoystickWidget(this, 'R');
112 ui->rightStickLayout->addWidget(vJoyRight);
114 connect(vJoyLeft, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
115 connect(vJoyLeft, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
116 connect(vJoyLeft, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
118 connect(vJoyRight, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
119 connect(vJoyRight, SIGNAL(trimButtonReleased()), this, SLOT(onTrimReleased()));
120 connect(vJoyRight, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
123 SimulatorWidget::~SimulatorWidget()
125 shutdown();
127 if (timer)
128 timer->deleteLater();
129 if (radioUiWidget)
130 delete radioUiWidget;
131 if (vJoyLeft)
132 delete vJoyLeft;
133 if (vJoyRight)
134 delete vJoyRight;
135 #ifdef JOYSTICKS
136 if (joystick)
137 delete joystick;
138 #endif
140 firmware = NULL; // Not sure we should delete this but at least release our pointer.
141 // NOTE : <simulator> should be deleted (or not) in the parent process which gave it to us in the first place.
143 delete ui;
148 * Public slots/setters
151 void SimulatorWidget::setSdPath(const QString & sdPath)
153 setPaths(sdPath, radioDataPath);
156 void SimulatorWidget::setDataPath(const QString & dataPath)
158 setPaths(sdCardPath, dataPath);
161 void SimulatorWidget::setPaths(const QString & sdPath, const QString & dataPath)
163 sdCardPath = sdPath;
164 radioDataPath = dataPath;
165 if (simulator)
166 simulator->setSdPath(sdPath, dataPath);
169 void SimulatorWidget::setRadioSettings(const GeneralSettings settings)
171 radioSettings = settings;
175 * This function can accept no parameters, a file name (QString is a QBA), or a data array. It will attempt to load radio settings data from one of
176 * several sources into a RadioData object, parse the data, and then pass it on as appropriate to the SimulatorInterface in start().
177 * If given no/blank <dataSource>, and setDataPath() was already called, then it will check that directory for "Horus-style" data files.
178 * If given a file name, set the <fromFile> parameter to 'true'. This will attempt to load radio settings from said file
179 * and later start the simulator interface in start() using the same data.
180 * If <dataSource> is a byte array of data, attempts to load radio settings from there and will also start the simulator interface
181 * with the same data when start() is called.
182 * If you already have a valid RadioData structure, call setRadioData() instead.
184 bool SimulatorWidget::setStartupData(const QByteArray & dataSource, bool fromFile)
186 RadioData simuData;
187 quint16 ret = 1;
188 QString error;
189 QString fileName(dataSource);
191 // If <dataSource> is blank but we have a data path, use that for individual radio/model files.
192 if (dataSource.isEmpty() && !radioDataPath.isEmpty()) {
193 // If directory structure already exists, try to load data from there.
194 // FIXME : need Storage class to return formal error code, not just a boolean, because it would be better
195 // to avoid hard-coding paths like "RADIO" here. E.g. did it fail due to no data at all, or corrupt data, or...?
196 if (QDir(QString(radioDataPath).append("/RADIO")).exists()) {
197 SdcardFormat sdcard(radioDataPath);
198 if (!(ret = sdcard.load(simuData))) {
199 error = sdcard.error();
203 // Supposedly we're being given a file name to use, try that out.
204 else if (fromFile && !fileName.isEmpty()) {
205 Storage store = Storage(fileName);
206 ret = store.load(simuData);
207 if (!ret && QFile(fileName).exists()) {
208 error = store.error();
210 else {
211 if (fileName.endsWith(".otx", Qt::CaseInsensitive)) {
212 // no radios can work with .otx files directly, so we load contents into either
213 // a temporary folder (Horus) or local data array (other radios) which we'll save back to .otx upon exit
214 if ((ret = setRadioData(&simuData))) {
215 startupFromFile = false;
216 return true;
219 else {
220 // the binary file will be read/written directly by the fw interface, save the file name for simulator->start()
221 startupData = dataSource;
225 // Assume a byte array of radio data was passed, load it.
226 else if (!dataSource.isEmpty()) {
227 ret = firmware->getEEpromInterface()->load(simuData, (uint8_t *)dataSource.constData(), getEEpromSize(m_board));
228 startupData = dataSource; // save the data for start()
230 // we're :-(
231 else {
232 ret = 0;
233 error = tr("Could not determine startup data source.");
236 if (!ret) {
237 if (error.isEmpty())
238 error = tr("Could not load data, possibly wrong format.");
239 QMessageBox::critical(this, tr("Data Load Error"), error);
240 return false;
243 radioSettings = simuData.generalSettings;
244 startupFromFile = fromFile;
246 return true;
249 bool SimulatorWidget::setRadioData(RadioData * radioData)
251 bool ret = true;
253 saveTempRadioData = (flags & SIMULATOR_FLAGS_STANDALONE);
255 if (IS_HORUS(m_board))
256 ret = useTempDataPath(true);
258 if (ret) {
259 if (radioDataPath.isEmpty()) {
260 startupData.fill(0, getEEpromSize(m_board));
261 if (firmware->getEEpromInterface()->save((uint8_t *)startupData.data(), *radioData, 0, firmware->getCapability(SimulatorVariant)) <= 0) {
262 ret = false;
265 else {
266 ret = saveRadioData(radioData, radioDataPath);
270 if (ret)
271 radioSettings = radioData->generalSettings;
273 return ret;
276 bool SimulatorWidget::setOptions(SimulatorOptions & options, bool withSave)
278 bool ret = false;
280 setSdPath(options.sdPath);
282 if (options.startupDataType == SimulatorOptions::START_WITH_FOLDER && !options.dataFolder.isEmpty()) {
283 setDataPath(options.dataFolder);
284 ret = setStartupData();
286 else if (options.startupDataType == SimulatorOptions::START_WITH_SDPATH && !options.sdPath.isEmpty()) {
287 setDataPath(options.sdPath);
288 ret = setStartupData();
290 else if (options.startupDataType == SimulatorOptions::START_WITH_FILE && !options.dataFile.isEmpty()) {
291 ret = setStartupData(options.dataFile.toLocal8Bit(), true);
293 else {
294 QString error = tr("Invalid startup data provided. Plese specify a proper file/path.");
295 QMessageBox::critical(this, tr("Simulator Startup Error"), error);
298 if (ret && withSave)
299 g.profile[radioProfileId].simulatorOptions(options);
301 return ret;
304 bool SimulatorWidget::saveRadioData(RadioData * radioData, const QString & path, QString * error)
306 QString dir = path;
307 if (dir.isEmpty())
308 dir = radioDataPath;
310 if (radioData && !dir.isEmpty()) {
311 SdcardFormat sdcard(dir);
312 bool ret = sdcard.write(*radioData);
313 if (!ret && error)
314 *error = sdcard.error();
315 return ret;
318 return false;
321 bool SimulatorWidget::useTempDataPath(bool deleteOnClose)
323 if (deleteTempRadioData)
324 deleteTempData();
326 QTemporaryDir tmpDir(QDir::tempPath() + "/otx-XXXXXX");
327 if (tmpDir.isValid()) {
328 setDataPath(tmpDir.path());
329 tmpDir.setAutoRemove(false);
330 deleteTempRadioData = deleteOnClose;
331 qDebug() << __FILE__ << __LINE__ << "Created temporary settings directory" << radioDataPath << "with delteOnClose:" << deleteOnClose;
332 return true;
334 else {
335 qDebug() << __FILE__ << __LINE__ << "ERROR : Failed to create temporary settings directory" << radioDataPath;
336 return false;
340 // This will save radio data from temporary folder structure back into an .otx file, eg. for Horus.
341 bool SimulatorWidget::saveTempData()
343 bool ret = false;
344 QString error;
345 QString file = g.profile[radioProfileId].simulatorOptions().dataFile;
347 if (!file.isEmpty()) {
348 RadioData radioData;
350 if (radioDataPath.isEmpty()) {
351 if (!startupData.isEmpty()) {
352 if (!QFile(file).exists()) {
353 QFile fh(file);
354 if (!fh.open(QIODevice::WriteOnly))
355 error = tr("Error saving data: could open file for writing: '%1'").arg(file);
356 else
357 fh.close();
360 if (!firmware->getEEpromInterface()->load(radioData, (uint8_t *)startupData.constData(), getEEpromSize(m_board))) {
361 error = tr("Error saving data: could not get data from simulator interface.");
363 else {
364 radioData.fixModelFilenames();
365 ret = true;
369 else {
370 SdcardFormat sdcard(radioDataPath);
371 if (!(ret = sdcard.load(radioData))) {
372 error = sdcard.error();
375 if (ret) {
376 Storage store(file);
377 if (!(ret = store.write(radioData)))
378 error = store.error();
379 else
380 qDebug() << __FILE__ << __LINE__ << "Saved radio data to file" << file;
384 if (!ret) {
385 if (error.isEmpty())
386 error = tr("An unexpected error occurred while attempting to save radio data to file '%1'.").arg(file);
387 QMessageBox::critical(this, tr("Data Save Error"), error);
390 return ret;
393 void SimulatorWidget::deleteTempData()
395 if (!radioDataPath.isEmpty()) {
396 QDir tpath(radioDataPath);
397 qDebug() << __FILE__ << __LINE__ << "Deleting temporary settings directory" << tpath.absolutePath();
398 tpath.removeRecursively();
399 tpath.rmdir(radioDataPath); // for some reason this is necessary to remove the base folder
403 void SimulatorWidget::saveState()
405 SimulatorOptions opts = g.profile[radioProfileId].simulatorOptions();
406 //opts.windowGeometry = saveGeometry();
407 opts.controlsState = saveRadioWidgetsState();
408 g.profile[radioProfileId].simulatorOptions(opts);
411 void SimulatorWidget::setUiAreaStyle(const QString & style)
413 setStyleSheet(style);
416 void SimulatorWidget::captureScreenshot(bool)
418 if (radioUiWidget)
419 radioUiWidget->captureScreenshot();
423 * Startup
426 void SimulatorWidget::start()
428 setupRadioWidgets();
429 setupJoysticks();
430 restoreRadioWidgetsState();
432 if (startupData.isEmpty())
433 simulator->start((const char *)0);
434 else if (startupFromFile)
435 simulator->start(startupData.constData());
436 else
437 simulator->start(startupData, (flags & SIMULATOR_FLAGS_NOTX) ? false : true);
439 setTrims();
440 getValues();
441 setupTimer();
444 void SimulatorWidget::stop()
446 timer->stop();
447 simulator->stop();
448 if (saveTempRadioData) {
449 startupData.fill(0, getEEpromSize(m_board));
450 simulator->readEepromData(startupData);
454 void SimulatorWidget::restart()
456 stop();
457 saveState();
458 setStartupData(startupData, startupFromFile);
459 start();
462 void SimulatorWidget::shutdown()
464 stop();
465 saveState();
466 if (saveTempRadioData)
467 saveTempData();
468 if (deleteTempRadioData)
469 deleteTempData();
473 * Setup
476 void SimulatorWidget::setRadioProfileId(int value)
478 radioProfileId = value;
479 if (simulator)
480 simulator->setVolumeGain(g.profile[radioProfileId].volumeGain());
483 void SimulatorWidget::setupRadioWidgets()
485 int i, midpos, aIdx;
486 QString wname;
487 Board::Type board = firmware->getBoard();
489 // First clear out any existing widgets.
490 if (switches.size()) {
491 foreach (RadioWidget * w, switches) {
492 ui->radioWidgetsHTLayout->removeWidget(w);
493 w->deleteLater();
495 switches.clear();
497 if (analogs.size()) {
498 foreach (RadioWidget * w, analogs) {
499 if (w->getType() == RadioWidget::RADIO_WIDGET_KNOB)
500 ui->radioWidgetsHTLayout->removeWidget(w);
501 else
502 ui->VCGridLayout->removeWidget(w);
503 w->deleteLater();
505 analogs.clear();
508 // Now set up new widgets.
510 // switches
511 Board::SwitchInfo switchInfo;
512 Board::SwitchType swcfg;
513 for (i = 0; i < getBoardCapability(board, Board::Switches) && i < CPN_MAX_SWITCHES; ++i) {
514 if (radioSettings.switchConfig[i] == Board::SWITCH_NOT_AVAILABLE)
515 continue;
517 swcfg = Board::SwitchType(radioSettings.switchConfig[i]);
519 if ((wname = QString(radioSettings.switchName[i])).isEmpty()) {
520 switchInfo = getSwitchInfo(board, i);
521 wname = QString(switchInfo.name);
523 RadioSwitchWidget * sw = new RadioSwitchWidget(swcfg, wname, -1, ui->radioWidgetsHT);
524 sw->setIndex(i);
525 ui->radioWidgetsHTLayout->addWidget(sw);
526 switches.append(sw);
529 midpos = (int)floorf(switches.size() / 2.0f);
530 aIdx = 0;
532 // pots in middle of switches
533 for (i = 0; i < getBoardCapability(board, Board::Pots) && i < CPN_MAX_POTS; ++i) {
534 if (!radioSettings.isPotAvailable(i))
535 continue;
537 if ((wname = QString(radioSettings.potName[i])).isEmpty())
538 wname = firmware->getAnalogInputName(4 + aIdx + i);
540 RadioKnobWidget * pot = new RadioKnobWidget(Board::PotType(radioSettings.potConfig[i]), wname, 0, ui->radioWidgetsHT);
541 pot->setIndex(aIdx + i);
542 // FIXME : total hack here -- this needs to follow the exception in radio/src/mixer.cpp:evalInputs()
543 if (i == 0 && IS_TARANIS(board) && !IS_TARANIS_X7(board))
544 pot->setInvertValue(true);
545 ui->radioWidgetsHTLayout->insertWidget(midpos++, pot);
546 analogs.append(pot);
549 aIdx += i;
551 // faders between sticks
552 int r = 0, c = 0;
553 for (i = 0; i < getBoardCapability(board, Board::Sliders) && i + aIdx < CPN_MAX_POTS; ++i) {
554 if (!radioSettings.isSliderAvailable(i))
555 continue;
557 if ((wname = QString(radioSettings.sliderName[i])).isEmpty())
558 wname = firmware->getAnalogInputName(4 + aIdx + i);
560 RadioFaderWidget * sl = new RadioFaderWidget(wname, 0, ui->radioWidgetsVC);
561 sl->setIndex(aIdx + i);
562 // FIXME : total hack here -- this needs to follow the exception in radio/src/mixer.cpp:evalInputs()
563 if (i == 0 && IS_TARANIS(board) && !IS_TARANIS_X7(board))
564 sl->setInvertValue(true);
565 /* 2-row option
566 if (!(i % 2)) {
567 ++r;
568 c = 0;
569 } */
570 ui->VCGridLayout->addWidget(sl, r, c++, 1, 1);
571 analogs.append(sl);
575 void SimulatorWidget::setupJoysticks()
577 #ifdef JOYSTICKS
578 static bool joysticksEnabled = false;
579 if (g.jsSupport() && g.jsCtrl() > -1) {
580 int count=0, axe;
581 for (int j=0; j < MAX_JOYSTICKS; j++){
582 axe = g.joystick[j].stick_axe();
583 if (axe >= 0 && axe < MAX_JOYSTICKS) {
584 jsmap[axe] = j + 1;
585 jscal[axe][0] = g.joystick[j].stick_min();
586 jscal[axe][1] = g.joystick[j].stick_med();
587 jscal[axe][2] = g.joystick[j].stick_max();
588 jscal[axe][3] = g.joystick[j].stick_inv();
589 count++;
592 if (count<3) {
593 QMessageBox::critical(this, tr("Warning"), tr("Joystick enabled but not configured correctly"));
594 return;
597 if (!joystick)
598 joystick = new Joystick(this);
599 else
600 joystick->close();
602 if (joystick && joystick->open(g.jsCtrl())) {
603 int numAxes = std::min(joystick->numAxes, MAX_JOYSTICKS);
604 for (int j=0; j<numAxes; j++) {
605 joystick->sensitivities[j] = 0;
606 joystick->deadzones[j] = 0;
608 //mode 1,3 -> THR on right
609 vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
610 vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
611 vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
612 vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
613 connect(joystick, SIGNAL(axisValueChanged(int, int)), this, SLOT(onjoystickAxisValueChanged(int, int)));
614 joysticksEnabled = true;
616 else {
617 QMessageBox::critical(this, tr("Warning"), tr("Cannot open joystick, joystick disabled"));
620 else if (joysticksEnabled && joystick) {
621 disconnect(joystick, 0, this, 0);
622 vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, false);
623 vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, false);
624 vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, false);
625 vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, false);
627 #endif
630 void SimulatorWidget::setupTimer()
632 if (timer) {
633 timer->stop();
634 disconnect(timer, 0, this, 0);
635 disconnect(timer, 0, radioUiWidget, 0);
636 timer->deleteLater();
637 timer = NULL;
639 timer = new QTimer(this);
640 connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent()));
641 connect(timer, SIGNAL(timeout()), radioUiWidget, SLOT(updateUi()));
643 timer->start(10);
646 void SimulatorWidget::restoreRadioWidgetsState()
648 RadioWidget::RadioWidgetState state;
649 QMap<int, QByteArray> switchesMap;
650 QMap<int, QByteArray> analogsMap;
651 QList<QByteArray> states = g.profile[radioProfileId].simulatorOptions().controlsState;
653 foreach (QByteArray ba, states) {
654 QDataStream stream(ba);
655 stream >> state;
656 if (state.type == RadioWidget::RADIO_WIDGET_SWITCH)
657 switchesMap.insert(state.index, ba);
658 else
659 analogsMap.insert(state.index, ba);
662 for (int i = 0; i < analogs.size(); ++i) {
663 if (analogsMap.contains(analogs[i]->getIndex()))
664 analogs[i]->setStateData(analogsMap.value(analogs[i]->getIndex()));
667 for (int i = 0; i < switches.size(); ++i) {
668 if (switchesMap.contains(switches[i]->getIndex()))
669 switches[i]->setStateData(switchesMap.value(switches[i]->getIndex()));
672 // Set throttle stick down and locked, side depends on mode
673 if (radioSettings.stickMode & 1) {
674 vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
675 vJoyLeft->setStickY(1);
677 else {
678 vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
679 vJoyRight->setStickY(1);
683 QList<QByteArray> SimulatorWidget::saveRadioWidgetsState()
685 QList<QByteArray> states;
687 for (int i = 0; i < analogs.size(); ++i)
688 states.append(analogs[i]->getStateData());
690 for (int i = 0; i < switches.size(); ++i)
691 states.append(switches[i]->getStateData());
693 return states;
697 * Input/Output handlers for SimulatorInterface
700 // Read various values from firmware simulator and populate values in this UI
701 void SimulatorWidget::setValues()
703 int currentPhase = simulator->getPhase();
705 // display current flight mode in window title
706 if (currentPhase != lastPhase) {
707 lastPhase = currentPhase;
708 QString phase_name = QString(simulator->getPhaseName(currentPhase));
709 if (phase_name.isEmpty())
710 phase_name = QString::number(currentPhase);
711 setWindowTitle(windowName + QString(" - Flight Mode %1").arg(phase_name));
715 // "get" values from this UI and send them to the firmware simulator.
716 void SimulatorWidget::getValues()
718 static const int numTrims = firmware->getCapability(NumTrimSwitches);
719 int i;
721 TxInputs inp;
722 memset(&inp, 0, sizeof(TxInputs));
724 inp.sticks[0] = int(1024 * vJoyLeft->getStickX()); // LEFT HORZ
725 inp.sticks[1] = int(-1024 * vJoyLeft->getStickY()); // LEFT VERT
726 inp.sticks[2] = int(-1024 * vJoyRight->getStickY()); // RGHT VERT
727 inp.sticks[3] = int(1024 * vJoyRight->getStickX()); // RGHT HORZ
729 for (i = 0; i < analogs.size() && i < CPN_MAX_POTS; ++i)
730 inp.pots[analogs[i]->getIndex()] = analogs[i]->getValue();
732 for (i = 0; i < switches.size() && i < CPN_MAX_SWITCHES; ++i)
733 inp.switches[switches[i]->getIndex()] = switches[i]->getValue();
735 for (i = 0; i < numTrims; ++i)
736 inp.trims[i] = (trimPressed == i);
738 foreach (RadioUiAction * act, radioUiWidget->getActions()) {
739 if (act->getIndex() > -1 && act->getIndex() < CPN_MAX_KEYS)
740 inp.keys[act->getIndex()] = act->isActive();
743 if (radioUiWidget->getRotEncAction())
744 inp.rotenc = radioUiWidget->getRotEncAction()->isActive();
746 simulator->setValues(inp);
749 // Read stick trim values from firmware simulator and set joystick widgets as needed.
750 void SimulatorWidget::setTrims()
752 typedef VirtualJoystickWidget VJW;
753 static Trims lastTrims;
754 Trims trims;
755 simulator->getTrims(trims);
757 if (trims.values[VJW::TRIM_AXIS_L_X] != lastTrims.values[VJW::TRIM_AXIS_L_X])
758 vJoyLeft->setTrimValue(VJW::TRIM_AXIS_L_X, trims.values[VJW::TRIM_AXIS_L_X]);
759 if (trims.values[VJW::TRIM_AXIS_L_Y] != lastTrims.values[VJW::TRIM_AXIS_L_Y])
760 vJoyLeft->setTrimValue(VJW::TRIM_AXIS_L_Y, trims.values[VJW::TRIM_AXIS_L_Y]);
761 if (trims.values[VJW::TRIM_AXIS_R_Y] != lastTrims.values[VJW::TRIM_AXIS_R_Y])
762 vJoyRight->setTrimValue(VJW::TRIM_AXIS_R_Y, trims.values[VJW::TRIM_AXIS_R_Y]);
763 if (trims.values[VJW::TRIM_AXIS_R_X] != lastTrims.values[VJW::TRIM_AXIS_R_X])
764 vJoyRight->setTrimValue(VJW::TRIM_AXIS_R_X, trims.values[VJW::TRIM_AXIS_R_X]);
766 if (trims.extended != lastTrims.extended) {
767 int trimMin = -125, trimMax = +125;
768 if (trims.extended) {
769 trimMin = -500;
770 trimMax = +500;
772 vJoyLeft->setTrimRange(VJW::TRIM_AXIS_L_X, trimMin, trimMax);
773 vJoyLeft->setTrimRange(VJW::TRIM_AXIS_L_Y, trimMin, trimMax);
774 vJoyRight->setTrimRange(VJW::TRIM_AXIS_R_Y, trimMin, trimMax);
775 vJoyRight->setTrimRange(VJW::TRIM_AXIS_R_X, trimMin, trimMax);
777 lastTrims = trims;
781 * Event handlers/private slots
784 //void SimulatorDialog::showEvent(QShowEvent *)
786 // if (firstShow && isVisible()) {
787 // firstShow = false;
788 // }
791 //void SimulatorDialog::closeEvent(QCloseEvent *)
795 void SimulatorWidget::mousePressEvent(QMouseEvent *event)
797 if (radioUiWidget)
798 radioUiWidget->mousePressEvent(event);
801 void SimulatorWidget::mouseReleaseEvent(QMouseEvent *event)
803 if (radioUiWidget)
804 radioUiWidget->mouseReleaseEvent(event);
807 void SimulatorWidget::wheelEvent(QWheelEvent *event)
809 if (radioUiWidget)
810 radioUiWidget->wheelEvent(event);
813 void SimulatorWidget::onTimerEvent()
815 static unsigned int lcd_counter = 0;
817 if (!simulator->timer10ms()) {
818 QMessageBox::critical(this, "Companion", tr("Firmware %1 error: %2").arg(firmware->getName()).arg(simulator->getError()));
819 timer->stop();
820 return;
823 getValues();
825 if (!(lcd_counter++ % 5)) {
826 setValues();
827 setTrims();
828 centerSticks();
832 void SimulatorWidget::onTrimPressed(int which)
834 trimPressed = which;
837 void SimulatorWidget::onTrimReleased()
839 trimPressed = TRIM_NONE;
842 void SimulatorWidget::onTrimSliderMoved(int which, int value)
844 simulator->setTrim(which, value);
847 void SimulatorWidget::centerSticks()
849 if (vJoyLeft)
850 vJoyLeft->centerStick();
852 if (vJoyRight)
853 vJoyRight->centerStick();
856 void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value)
858 #ifdef JOYSTICKS
859 int stick;
860 if (axis>=0 && axis<=8) {
861 stick=jsmap[axis];
862 int stickval;
863 if (value>jscal[axis][1]) {
864 if ((jscal[axis][2]-jscal[axis][1])==0)
865 return;
866 stickval=(1024*(value-jscal[axis][1]))/(jscal[axis][2]-jscal[axis][1]);
868 else {
869 if ((jscal[axis][1]-jscal[axis][0])==0)
870 return;
871 stickval=(1024*(value-jscal[axis][1]))/(jscal[axis][1]-jscal[axis][0]);
873 if (jscal[axis][3]==1) {
874 stickval*=-1;
876 if (stick==1 ) {
877 vJoyRight->setStickY(-stickval/1024.0);
879 else if (stick==2) {
880 vJoyRight->setStickX(stickval/1024.0);
882 else if (stick==3) {
883 vJoyLeft->setStickY(-stickval/1024.0);
885 else if (stick==4) {
886 vJoyLeft->setStickX(stickval/1024.0);
888 else if (stick >= 5 && stick < 5 + analogs.count()) {
889 analogs[stick-5]->setValue(stickval);
892 #endif