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"
25 #include "radiofaderwidget.h"
26 #include "radioknobwidget.h"
27 #include "radioswitchwidget.h"
28 #include "radiouiaction.h"
30 #include "simulateduiwidget.h"
31 #include "simulatorinterface.h"
33 #include "virtualjoystickwidget.h"
36 #include "joystickdialog.h"
43 SimulatorWidget::SimulatorWidget(QWidget
* parent
, SimulatorInterface
*simulator
, quint8 flags
):
45 ui(new Ui::SimulatorWidget
),
47 firmware(getCurrentFirmware()),
48 radioSettings(GeneralSettings()),
53 m_board(getCurrentBoard()),
57 trimPressed(TRIM_NONE
),
58 startupFromFile(false),
59 deleteTempRadioData(false),
60 saveTempRadioData(false),
61 middleButtonPressed(false),
70 setRadioProfileId(g
.sessionId());
71 setSdPath(g
.profile
[radioProfileId
].sdPath());
75 windowName
= tr("Radio Simulator (%1)").arg(firmware
->getName());
76 setWindowTitle(windowName
);
79 case Board::BOARD_TARANIS_X7
:
80 radioUiWidget
= new SimulatedUIWidgetX7(simulator
, this);
82 case Board::BOARD_TARANIS_X9D
:
83 case Board::BOARD_TARANIS_X9DP
:
84 radioUiWidget
= new SimulatedUIWidgetX9(simulator
, this);
86 case Board::BOARD_TARANIS_X9E
:
87 radioUiWidget
= new SimulatedUIWidgetX9E(simulator
, this);
89 case Board::BOARD_X12S
:
90 case Board::BOARD_X10
:
91 radioUiWidget
= new SimulatedUIWidgetX12(simulator
, this);
94 radioUiWidget
= new SimulatedUIWidget9X(simulator
, this);
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()
128 timer
->deleteLater();
130 delete radioUiWidget
;
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.
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
)
164 radioDataPath
= dataPath
;
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
)
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();
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;
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()
233 error
= tr("Could not determine startup data source.");
238 error
= tr("Could not load data, possibly wrong format.");
239 QMessageBox::critical(this, tr("Data Load Error"), error
);
243 radioSettings
= simuData
.generalSettings
;
244 startupFromFile
= fromFile
;
249 bool SimulatorWidget::setRadioData(RadioData
* radioData
)
253 saveTempRadioData
= (flags
& SIMULATOR_FLAGS_STANDALONE
);
255 if (IS_HORUS(m_board
))
256 ret
= useTempDataPath(true);
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) {
266 ret
= saveRadioData(radioData
, radioDataPath
);
271 radioSettings
= radioData
->generalSettings
;
276 bool SimulatorWidget::setOptions(SimulatorOptions
& options
, bool withSave
)
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);
294 QString error
= tr("Invalid startup data provided. Plese specify a proper file/path.");
295 QMessageBox::critical(this, tr("Simulator Startup Error"), error
);
299 g
.profile
[radioProfileId
].simulatorOptions(options
);
304 bool SimulatorWidget::saveRadioData(RadioData
* radioData
, const QString
& path
, QString
* error
)
310 if (radioData
&& !dir
.isEmpty()) {
311 SdcardFormat
sdcard(dir
);
312 bool ret
= sdcard
.write(*radioData
);
314 *error
= sdcard
.error();
321 bool SimulatorWidget::useTempDataPath(bool deleteOnClose
)
323 if (deleteTempRadioData
)
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
;
335 qDebug() << __FILE__
<< __LINE__
<< "ERROR : Failed to create temporary settings directory" << radioDataPath
;
340 // This will save radio data from temporary folder structure back into an .otx file, eg. for Horus.
341 bool SimulatorWidget::saveTempData()
345 QString file
= g
.profile
[radioProfileId
].simulatorOptions().dataFile
;
347 if (!file
.isEmpty()) {
350 if (radioDataPath
.isEmpty()) {
351 if (!startupData
.isEmpty()) {
352 if (!QFile(file
).exists()) {
354 if (!fh
.open(QIODevice::WriteOnly
))
355 error
= tr("Error saving data: could open file for writing: '%1'").arg(file
);
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.");
364 radioData
.fixModelFilenames();
370 SdcardFormat
sdcard(radioDataPath
);
371 if (!(ret
= sdcard
.load(radioData
))) {
372 error
= sdcard
.error();
377 if (!(ret
= store
.write(radioData
)))
378 error
= store
.error();
380 qDebug() << __FILE__
<< __LINE__
<< "Saved radio data to file" << file
;
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
);
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)
419 radioUiWidget
->captureScreenshot();
426 void SimulatorWidget::start()
430 restoreRadioWidgetsState();
432 if (startupData
.isEmpty())
433 simulator
->start((const char *)0);
434 else if (startupFromFile
)
435 simulator
->start(startupData
.constData());
437 simulator
->start(startupData
, (flags
& SIMULATOR_FLAGS_NOTX
) ? false : true);
444 void SimulatorWidget::stop()
448 if (saveTempRadioData
) {
449 startupData
.fill(0, getEEpromSize(m_board
));
450 simulator
->readEepromData(startupData
);
454 void SimulatorWidget::restart()
458 setStartupData(startupData
, startupFromFile
);
462 void SimulatorWidget::shutdown()
466 if (saveTempRadioData
)
468 if (deleteTempRadioData
)
476 void SimulatorWidget::setRadioProfileId(int value
)
478 radioProfileId
= value
;
480 simulator
->setVolumeGain(g
.profile
[radioProfileId
].volumeGain());
483 void SimulatorWidget::setupRadioWidgets()
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
);
497 if (analogs
.size()) {
498 foreach (RadioWidget
* w
, analogs
) {
499 if (w
->getType() == RadioWidget::RADIO_WIDGET_KNOB
)
500 ui
->radioWidgetsHTLayout
->removeWidget(w
);
502 ui
->VCGridLayout
->removeWidget(w
);
508 // Now set up new widgets.
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
)
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
);
525 ui
->radioWidgetsHTLayout
->addWidget(sw
);
529 midpos
= (int)floorf(switches
.size() / 2.0f
);
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
))
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
);
551 // faders between sticks
553 for (i
= 0; i
< getBoardCapability(board
, Board::Sliders
) && i
+ aIdx
< CPN_MAX_POTS
; ++i
) {
554 if (!radioSettings
.isSliderAvailable(i
))
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);
570 ui
->VCGridLayout
->addWidget(sl
, r
, c
++, 1, 1);
575 void SimulatorWidget::setupJoysticks()
578 static bool joysticksEnabled
= false;
579 if (g
.jsSupport() && g
.jsCtrl() > -1) {
581 for (int j
=0; j
< MAX_JOYSTICKS
; j
++){
582 axe
= g
.joystick
[j
].stick_axe();
583 if (axe
>= 0 && axe
< MAX_JOYSTICKS
) {
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();
593 QMessageBox::critical(this, tr("Warning"), tr("Joystick enabled but not configured correctly"));
598 joystick
= new Joystick(this);
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;
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);
630 void SimulatorWidget::setupTimer()
634 disconnect(timer
, 0, this, 0);
635 disconnect(timer
, 0, radioUiWidget
, 0);
636 timer
->deleteLater();
639 timer
= new QTimer(this);
640 connect(timer
, SIGNAL(timeout()), this, SLOT(onTimerEvent()));
641 connect(timer
, SIGNAL(timeout()), radioUiWidget
, SLOT(updateUi()));
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
);
656 if (state
.type
== RadioWidget::RADIO_WIDGET_SWITCH
)
657 switchesMap
.insert(state
.index
, ba
);
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);
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());
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
);
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
;
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
) {
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
);
781 * Event handlers/private slots
784 //void SimulatorDialog::showEvent(QShowEvent *)
786 // if (firstShow && isVisible()) {
787 // firstShow = false;
791 //void SimulatorDialog::closeEvent(QCloseEvent *)
795 void SimulatorWidget::mousePressEvent(QMouseEvent
*event
)
798 radioUiWidget
->mousePressEvent(event
);
801 void SimulatorWidget::mouseReleaseEvent(QMouseEvent
*event
)
804 radioUiWidget
->mouseReleaseEvent(event
);
807 void SimulatorWidget::wheelEvent(QWheelEvent
*event
)
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()));
825 if (!(lcd_counter
++ % 5)) {
832 void SimulatorWidget::onTrimPressed(int 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()
850 vJoyLeft
->centerStick();
853 vJoyRight
->centerStick();
856 void SimulatorWidget::onjoystickAxisValueChanged(int axis
, int value
)
860 if (axis
>=0 && axis
<=8) {
863 if (value
>jscal
[axis
][1]) {
864 if ((jscal
[axis
][2]-jscal
[axis
][1])==0)
866 stickval
=(1024*(value
-jscal
[axis
][1]))/(jscal
[axis
][2]-jscal
[axis
][1]);
869 if ((jscal
[axis
][1]-jscal
[axis
][0])==0)
871 stickval
=(1024*(value
-jscal
[axis
][1]))/(jscal
[axis
][1]-jscal
[axis
][0]);
873 if (jscal
[axis
][3]==1) {
877 vJoyRight
->setStickY(-stickval
/1024.0);
880 vJoyRight
->setStickX(stickval
/1024.0);
883 vJoyLeft
->setStickY(-stickval
/1024.0);
886 vJoyLeft
->setStickX(stickval
/1024.0);
888 else if (stick
>= 5 && stick
< 5 + analogs
.count()) {
889 analogs
[stick
-5]->setValue(stickval
);