ffbenvelopeparameters.cpp \
ffbperiodiceffectparameters.cpp \
ffbperiodiceffect.cpp \
- ffbnulleffect.cpp
+ ffbnulleffect.cpp \
+ conditioneffectsettings.cpp \
+ ffbconditioneffectparameters.cpp \
+ ffbconditioneffect.cpp
HEADERS += mainwindow.h \
deviceprober.h \
ffbenvelopeparameters.h \
ffbperiodiceffectparameters.h \
ffbperiodiceffect.h \
- ffbnulleffect.h
+ ffbnulleffect.h \
+ conditioneffectsettings.h \
+ ffbconditioneffectparameters.h \
+ ffbconditioneffect.h
FORMS += mainwindow.ui \
constanteffectsettings.ui \
envelopesettings.ui \
- periodiceffectsettings.ui
+ periodiceffectsettings.ui \
+ conditioneffectsettings.ui
QMAKE_CXXFLAGS += -std=c++11 -Wall
--- /dev/null
+#include "conditioneffectsettings.h"
+#include "ui_conditioneffectsettings.h"
+
+ConditionEffectSettings::ConditionEffectSettings(QWidget* parent) :
+ EffectSettings(parent),
+ ui(new Ui::ConditionEffectSettings)
+{
+ ui->setupUi(this);
+}
+
+void ConditionEffectSettings::fillAvailableSubtypesList(const QStringList& list)
+{
+ ui->cbox_subtype->clear();
+ ui->cbox_subtype->addItems(list);
+}
+
+bool ConditionEffectSettings::fillFromParameters(const std::shared_ptr<FFBEffectParameters> params)
+{
+ try {
+ const std::shared_ptr<FFBConditionEffectParameters> cdParams = std::dynamic_pointer_cast<FFBConditionEffectParameters>(params);
+ return fillFromParameters(cdParams);
+ } catch (std::bad_cast& ex) {
+ qCritical(ex.what());
+ return false;
+ }
+}
+
+bool ConditionEffectSettings::fillFromParameters(const std::shared_ptr<FFBConditionEffectParameters> cdParams)
+{
+ ui->qle_center->setText(QString::number(cdParams->center));
+ ui->qle_deadband->setText(QString::number(cdParams->deadband));
+ ui->qle_leftCoeff->setText(QString::number(cdParams->leftCoeff));
+ ui->qle_rightCoeff->setText(QString::number(cdParams->rightCoeff));
+ ui->qle_leftSat->setText(QString::number(cdParams->leftSat));
+ ui->qle_rightSat->setText(QString::number(cdParams->rightCoeff));
+ return true;
+}
+
+QString ConditionEffectSettings::center() const
+{
+ return ui->qle_center->text();
+}
+
+QString ConditionEffectSettings::deadband() const
+{
+ return ui->qle_deadband->text();
+}
+
+QString ConditionEffectSettings::leftCoeff() const
+{
+ return ui->qle_leftCoeff->text();
+}
+
+QString ConditionEffectSettings::rightCoeff() const
+{
+ return ui->qle_rightCoeff->text();
+}
+
+QString ConditionEffectSettings::leftSat() const
+{
+ return ui->qle_leftSat->text();
+}
+
+QString ConditionEffectSettings::rightSat() const
+{
+ return ui->qle_rightSat->text();
+}
+
+int ConditionEffectSettings::subtypeIdx() const
+{
+ return ui->cbox_subtype->currentIndex();
+}
+
+ConditionEffectSettings::~ConditionEffectSettings()
+{
+ delete ui;
+}
--- /dev/null
+#ifndef CONDITIONEFFECTSETTINGS_H
+#define CONDITIONEFFECTSETTINGS_H
+
+#include "effectsettings.h"
+#include "ffbconditioneffectparameters.h"
+
+namespace Ui {
+ class ConditionEffectSettings;
+}
+
+class ConditionEffectSettings : public EffectSettings
+{
+ Q_OBJECT
+public:
+ explicit ConditionEffectSettings(QWidget* parent = nullptr);
+ ~ConditionEffectSettings();
+ void fillAvailableSubtypesList(const QStringList& list);
+ bool fillFromParameters(const std::shared_ptr<FFBEffectParameters> params);
+ bool fillFromParameters(const std::shared_ptr<FFBConditionEffectParameters> cdParams);
+ QString center() const;
+ QString deadband() const;
+ QString leftCoeff() const;
+ QString rightCoeff() const;
+ QString leftSat() const;
+ QString rightSat() const;
+ int subtypeIdx() const;
+
+private:
+ Ui::ConditionEffectSettings* ui;
+};
+
+#endif // CONDITIONEFFECTSETTINGS_H
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConditionEffectSettings</class>
+ <widget class="QWidget" name="ConditionEffectSettings">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>210</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="ql_leftSat">
+ <property name="text">
+ <string>Left saturation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="qle_leftSat"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="ql_rightSat">
+ <property name="text">
+ <string>Right saturation:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="qle_rightSat"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="ql_leftCoeff">
+ <property name="text">
+ <string>Left coefficient:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="qle_leftCoeff"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="ql_rightCoeff">
+ <property name="text">
+ <string>Right coefficient:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="qle_rightCoeff"/>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="ql_deadband">
+ <property name="text">
+ <string>Deadband:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="ql_center">
+ <property name="text">
+ <string>Center:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="qle_deadband"/>
+ </item>
+ <item row="6" column="1">
+ <widget class="QLineEdit" name="qle_center"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="ql_subtype">
+ <property name="text">
+ <string>Subtype:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cbox_subtype"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
Q_OBJECT
public:
- explicit ConstantEffectSettings(QWidget* parent = 0);
+ explicit ConstantEffectSettings(QWidget* parent = nullptr);
~ConstantEffectSettings();
const EnvelopeSettings* envelopeSettings() const;
bool fillFromParameters(const std::shared_ptr<FFBEffectParameters> params);
--- /dev/null
+#include "ffbconditioneffect.h"
+
+FFBConditionEffect::FFBConditionEffect() :
+ FFBEffect(FFBEffectTypes::CONDITION)
+{}
+
+struct ff_effect* FFBConditionEffect::createFFStruct()
+{
+ struct ff_effect* eff = FFBEffect::createFFStruct(m_params);
+
+ eff->u.condition[0].center = m_params->center;
+ eff->u.condition[0].deadband = m_params->deadband;
+ eff->u.condition[0].left_coeff = m_params->leftCoeff;
+ eff->u.condition[0].right_coeff = m_params->rightCoeff;
+ eff->u.condition[0].left_saturation = m_params->leftSat;
+ eff->u.condition[0].right_saturation = m_params->rightSat;
+
+ switch (m_params->subtype) {
+ case ConditionSubtypes::DAMPER:
+ eff->type = FF_DAMPER;
+ break;
+ case ConditionSubtypes::FRICTION:
+ eff->type = FF_FRICTION;
+ break;
+ case ConditionSubtypes::INERTIA:
+ eff->type = FF_INERTIA;
+ break;
+ case ConditionSubtypes::SPRING:
+ eff->type = FF_SPRING;
+ break;
+ default:
+ qCritical("Unknown subtype");
+ delete eff;
+ return nullptr;
+ }
+
+ return eff;
+}
+
+bool FFBConditionEffect::setParameters(const std::shared_ptr<FFBEffectParameters> params)
+{
+ try {
+ const std::shared_ptr<FFBConditionEffectParameters> cdParams = std::dynamic_pointer_cast<FFBConditionEffectParameters>(params);
+ return setParameters(cdParams);
+ } catch (std::bad_cast& ex) {
+ qCritical(ex.what());
+ return false;
+ }
+ return false;
+}
+
+bool FFBConditionEffect::setParameters(const std::shared_ptr<FFBConditionEffectParameters> params)
+{
+ if (!checkGenericParameters(params))
+ return false;
+
+ if (!checkBoundsInclusive(m_params->center, -0x7FFF, 0x7FFF)) {
+ reportError("Center out of bounds,");
+ return false;
+ }
+
+ if (!checkBoundsInclusive(params->deadband, 0, 0xFFFF)) {
+ reportError("Deadband out of bounds.");
+ return false;
+ }
+
+ if (!checkBoundsInclusive(params->leftCoeff, -0x7FFF, 0x7FFF)) {
+ reportError("Left coefficient out of bounds.");
+ return false;
+ }
+
+ if (!checkBoundsInclusive(params->rightCoeff, -0x7FFF, 0x7FFF)) {
+ reportError("Right coefficient out of bounds.");
+ return false;
+ }
+
+ if (!checkBoundsInclusive(params->leftSat, 0, 0xFFFF)) {
+ reportError("Left saturation out of bounds.");
+ return false;
+ }
+
+ if (!checkBoundsInclusive(params->rightSat, 0, 0xFFFF)) {
+ reportError("Right saturation out of bounds.");
+ return false;
+ }
+
+ if (params->subtype == ConditionSubtypes::NONE) {
+ reportError("Invalid subtype");
+ return false;
+ }
+
+ m_params = params;
+ return true;
+}
--- /dev/null
+#ifndef FFBCONDITIONEFFECT_H
+#define FFBCONDITIONEFFECT_H
+
+#include "ffbeffect.h"
+#include "ffbconditioneffectparameters.h"
+
+class FFBConditionEffect : public FFBEffect
+{
+public:
+ explicit FFBConditionEffect();
+ struct ff_effect* createFFStruct();
+ inline const std::shared_ptr<FFBEffectParameters> parameters() const { return m_params; }
+ bool setParameters(const std::shared_ptr<FFBEffectParameters> params);
+ bool setParameters(const std::shared_ptr<FFBConditionEffectParameters> params);
+
+private:
+ std::shared_ptr<FFBConditionEffectParameters> m_params;
+};
+
+#endif // FFBCONDITIONEFFECT_H
--- /dev/null
+#include "ffbconditioneffectparameters.h"
+
+FFBConditionEffectParameters::FFBConditionEffectParameters() :
+ FFBEffectParameters(),
+ center(0),
+ deadband(0),
+ leftCoeff(0),
+ rightCoeff(0),
+ leftSat(0),
+ rightSat(0)
+{}
+
+bool FFBConditionEffectParameters::centerFromString(const QString& center)
+{
+ return qstringToInt(this->center, center);
+}
+
+bool FFBConditionEffectParameters::deadbandFromString(const QString& deadband)
+{
+ return qstringToInt(this->deadband, deadband);
+}
+
+bool FFBConditionEffectParameters::leftCoeffFromString(const QString& leftCoeff)
+{
+ return qstringToInt(this->leftCoeff, leftCoeff);
+}
+
+bool FFBConditionEffectParameters::rightCoeffFromString(const QString& rightCoeff)
+{
+ return qstringToInt(this->rightCoeff, rightCoeff);
+}
+
+bool FFBConditionEffectParameters::leftSatFromString(const QString& leftSat)
+{
+ return qstringToInt(this->leftSat, leftSat);
+}
+
+bool FFBConditionEffectParameters::rightSatFromString(const QString& rightSat)
+{
+ return qstringToInt(this->rightSat, rightSat);
+}
+
+
--- /dev/null
+#ifndef FFBCONDITIONEFFECTPARAMETERS_H
+#define FFBCONDITIONEFFECTPARAMETERS_H
+
+#include "ffbeffectparameters.h"
+
+class FFBConditionEffectParameters : public FFBEffectParameters
+{
+public:
+ FFBConditionEffectParameters();
+ bool centerFromString(const QString& center);
+ bool deadbandFromString(const QString& deadband);
+ bool leftCoeffFromString(const QString& leftCoeff);
+ bool rightCoeffFromString(const QString& rightCoeff);
+ bool leftSatFromString(const QString& leftSat);
+ bool rightSatFromString(const QString& rightSat);
+ inline void subtypeFromIdx(const ConditionSubtypes subtype) { this->subtype = subtype; }
+
+ int center;
+ int deadband;
+ int leftCoeff;
+ int rightCoeff;
+ int leftSat;
+ int rightSat;
+ ConditionSubtypes subtype;
+private:
+ inline bool qstringToInt(int& val, const QString& str) {
+ bool ok = false;
+ val = str.toInt(&ok);
+ return ok;
+ }
+};
+
+#endif // FFBCONDITIONEFFECTPARAMETERS_H
m_effects.push_back(FFBEffectFactory::createEffect(FFBEffectTypes::NONE));
}
+QStringList FFBDevice::availableConditionSubtypesList() const
+{
+ QStringList list;
+
+ for (const ConditionSubtypes s : m_availableConditionSubtypes)
+ list << conditionSubtypeName(s);
+
+ return list;
+}
+
QStringList FFBDevice::availableEffectsList() const
{
QStringList list;
return list;
}
+
+QString FFBDevice::conditionSubtypeName(const ConditionSubtypes subtype) const
+{
+ switch (subtype) {
+ case ConditionSubtypes::DAMPER:
+ return "Damper";
+ case ConditionSubtypes::FRICTION:
+ return "Friction";
+ case ConditionSubtypes::INERTIA:
+ return "Inertia";
+ case ConditionSubtypes::SPRING:
+ return "Spring";
+ default:
+ return "Unknown subtype";
+ }
+}
+
QString FFBDevice::effectName(const FFBEffectTypes effect) const
{
switch (effect) {
return "Periodic force";
case FFBEffectTypes::RAMP:
return "Ramp";
- case FFBEffectTypes::SPRING:
- return "Spring";
- case FFBEffectTypes::FRICTION:
- return "Friction";
- case FFBEffectTypes::DAMPER:
- return "Damper";
+ case FFBEffectTypes::CONDITION:
+ return "Condition";
case FFBEffectTypes::RUMBLE:
return "Rumble";
- case FFBEffectTypes::INERTIA:
- return "Inertia";
default:
return "Unknown effect";
}
m_availableEffects.push_back(FFBEffectTypes::PERIODIC);
if (testBit(FF_RAMP, caps))
m_availableEffects.push_back(FFBEffectTypes::RAMP);
- if (testBit(FF_SPRING, caps))
- m_availableEffects.push_back(FFBEffectTypes::SPRING);
- if (testBit(FF_FRICTION, caps))
- m_availableEffects.push_back(FFBEffectTypes::FRICTION);
- if (testBit(FF_DAMPER, caps))
- m_availableEffects.push_back(FFBEffectTypes::DAMPER);
- if (testBit(FF_INERTIA, caps))
- m_availableEffects.push_back(FFBEffectTypes::INERTIA);
+ if (testBit(FF_SPRING, caps) || testBit(FF_FRICTION, caps) ||
+ testBit(FF_DAMPER, caps) || testBit(FF_INERTIA, caps))
+ m_availableEffects.push_back(FFBEffectTypes::CONDITION);
/* Query waveforms for PERIODIC if the device supports it */
if (hasEffect(FFBEffectTypes::PERIODIC)) {
m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SAW_DOWN);
}
+ /* Query condition effect subtypes */
+ if (testBit(FF_SPRING, caps))
+ m_availableConditionSubtypes.push_back(ConditionSubtypes::SPRING);
+ if (testBit(FF_FRICTION, caps))
+ m_availableConditionSubtypes.push_back(ConditionSubtypes::FRICTION);
+ if (testBit(FF_DAMPER, caps))
+ m_availableConditionSubtypes.push_back(ConditionSubtypes::DAMPER);
+ if (testBit(FF_INERTIA, caps))
+ m_availableConditionSubtypes.push_back(ConditionSubtypes::INERTIA);
+
return true;
}
public:
explicit FFBDevice(const int fd, const QString& id, const int maxEffectCount, QObject* parent = 0);
+ QStringList availableConditionSubtypesList() const;
QStringList availableEffectsList() const;
QStringList availableWaveformsList() const;
+ inline ConditionSubtypes conditionSubtypeByIdx(const int idx) { return m_availableConditionSubtypes[idx]; }
+ QString conditionSubtypeName(const ConditionSubtypes subtype) const;
QString effectName(const FFBEffectTypes effect) const;
const std::shared_ptr<FFBEffectParameters> effectParameters(const int idx);
FFBEffect::FFBEffectStatus effectStatusByIdx(const int idx) const;
bool isEffectUpdateable(const std::shared_ptr<FFBEffect> effect, const std::shared_ptr<FFBEffectParameters> params, const FFBEffectTypes type);
bool removeEffect(const int idx);
int uploadEffect(struct ff_effect* effect);
+ std::vector<ConditionSubtypes> m_availableConditionSubtypes;
std::vector<FFBEffectTypes> m_availableEffects;
std::vector<PeriodicWaveforms> m_availablePeriodicWaveforms;
std::vector<std::shared_ptr<FFBEffect>> m_effects;
return std::shared_ptr<FFBEffect>(new FFBConstantEffect());
case FFBEffectTypes::PERIODIC:
return std::shared_ptr<FFBEffect>(new FFBPeriodicEffect());
+ case FFBEffectTypes::CONDITION:
+ return std::shared_ptr<FFBEffect>(new FFBConditionEffect());
default:
return nullptr;
}
#define FFBEFFECTFACTORY_H
#include "globals.h"
+#include "ffbconditioneffect.h"
#include "ffbconstanteffect.h"
#include "ffbnulleffect.h"
#include "ffbperiodiceffect.h"
static const int APP_VERSION_MINOR(1);
static const char APP_VERSION_REL('a');
-enum class FFBEffectTypes { NONE, CONSTANT, PERIODIC, RAMP, SPRING, FRICTION, DAMPER, RUMBLE, INERTIA };
+enum class FFBEffectTypes { NONE, CONSTANT, PERIODIC, RAMP, CONDITION, RUMBLE};
enum class PeriodicWaveforms { NONE, SQUARE, TRIANGLE, SINE, SAW_UP, SAW_DOWN };
+enum class ConditionSubtypes { NONE, SPRING, FRICTION, DAMPER, INERTIA };
template<typename T> inline bool checkBoundsInclusive(const T& val, const T& min, const T& max)
{
m_constantEffSet = new ConstantEffectSettings();
m_periodicEffSet = new PeriodicEffectSettings();
+ m_conditionEffSet = new ConditionEffectSettings();
+ ui->qstw_effectSpecifics->addWidget(m_conditionEffSet);
ui->qstw_effectSpecifics->addWidget(m_constantEffSet);
ui->qstw_effectSpecifics->addWidget(m_periodicEffSet);
return m_constantEffSet;
case FFBEffectTypes::PERIODIC:
return m_periodicEffSet;
+ case FFBEffectTypes::CONDITION:
+ return m_conditionEffSet;
default:
abort();
}
ui->cbox_devices->clear();
ui->cbox_devices->addItems(m_prober->listDevicesByID());
}
+
void MainWindow::fillEffectSlotsList(const int idx)
{
ui->cbox_effectSlots->clear();
fillEffectSlotsList(m_activeDevice->maxEffectCount());
fillEffectTypesList(m_activeDevice->availableEffectsList());
+ m_conditionEffSet->fillAvailableSubtypesList(m_activeDevice->availableConditionSubtypesList());
m_periodicEffSet->fillAvailableWaveformsList(m_activeDevice->availableWaveformsList());
}
params = iparams;
break;
}
+ case FFBEffectTypes::CONDITION:
+ {
+ std::shared_ptr<FFBConditionEffectParameters> cdParams = std::shared_ptr<FFBConditionEffectParameters>(new FFBConditionEffectParameters);
+ if (!readGeneralEffectParameters(params))
+ return false;
+ if (!cdParams->centerFromString(m_conditionEffSet->center())) {
+ QMessageBox::warning(this, res_inputFormatErrCap, "Invalid data in field \"Center\"");
+ return false;
+ }
+ if (!cdParams->deadbandFromString(m_conditionEffSet->deadband())) {
+ QMessageBox::warning(this, res_inputFormatErrCap, "Invalid data in field \"Deadband\"");
+ return false;
+ }
+ if (!cdParams->leftCoeffFromString(m_conditionEffSet->leftCoeff())) {
+ QMessageBox::warning(this, res_inputFormatErrCap, "Invalid data in field \"Left coefficient\"");
+ return false;
+ }
+ if (!cdParams->rightCoeffFromString(m_conditionEffSet->rightCoeff())) {
+ QMessageBox::warning(this, res_inputFormatErrCap, "Invalid data in field \"Right coefficient\"");
+ return false;
+ }
+ if (!cdParams->leftSatFromString(m_conditionEffSet->leftSat())) {
+ QMessageBox::warning(this, res_inputFormatErrCap, "Invalid data in field \"Left saturation\"");
+ return false;
+ }
+ if (!cdParams->rightSatFromString(m_conditionEffSet->rightSat())) {
+ QMessageBox::warning(this, res_inputFormatErrCap, "Invalid data in field \"Right saturation\"");
+ return false;
+ }
+
+ ConditionSubtypes subtype = m_activeDevice->conditionSubtypeByIdx(m_conditionEffSet->subtypeIdx());
+ cdParams->subtypeFromIdx(subtype);
+
+ params = cdParams;
+ }
default:
qDebug() << "Unhandled type of effect";
return false;
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+#include "conditioneffectsettings.h"
#include "constanteffectsettings.h"
#include "deviceprober.h"
#include "ffbconstanteffectparameters.h"
void setEffectStatusText(const FFBEffect::FFBEffectStatus status);
std::shared_ptr<FFBDevice> m_activeDevice;
+ ConditionEffectSettings* m_conditionEffSet;
ConstantEffectSettings* m_constantEffSet;
PeriodicEffectSettings* m_periodicEffSet;
std::shared_ptr<DeviceProber> m_prober;