kopia lustrzana https://gitlab.com/eliggett/wfview
Add support for Xencelabs QuickKeys
rodzic
80838e4f82
commit
7e89e9f457
|
@ -10,6 +10,12 @@ controllerSetup::controllerSetup(QWidget* parent) :
|
|||
scene = new controllerScene();
|
||||
connect(scene, SIGNAL(mousePressed(QPoint)), this, SLOT(mousePressed(QPoint)));
|
||||
ui->graphicsView->setScene(scene);
|
||||
ui->qkBrightCombo->setVisible(false);
|
||||
ui->qkOrientCombo->setVisible(false);
|
||||
ui->qkSpeedCombo->setVisible(false);
|
||||
ui->qkColorButton->setVisible(false);
|
||||
ui->qkTimeoutSpin->setVisible(false);
|
||||
ui->qkTimeoutLabel->setVisible(false);
|
||||
textItem = scene->addText("No USB controller found");
|
||||
textItem->setDefaultTextColor(Qt::gray);
|
||||
this->resize(this->sizeHint());
|
||||
|
@ -131,9 +137,11 @@ void controllerSetup::onEventIndexChanged(int index) {
|
|||
QMutexLocker locker(mutex);
|
||||
currentButton->onCommand = &commands->at(onEvent->currentData().toInt());
|
||||
currentButton->onText->setPlainText(currentButton->onCommand->text);
|
||||
currentButton->onText->setPos(currentButton->pos.center().x() - currentButton->onText->boundingRect().width() / 2,
|
||||
(currentButton->pos.center().y() - currentButton->onText->boundingRect().height() / 2)-6);
|
||||
// Signal that any button programming on the device should be completed.
|
||||
emit programButton(currentButton->num, currentButton->onCommand->text);
|
||||
}
|
||||
// Signal that any button programming on the device should be completed.
|
||||
emit programButton(onEvent->currentData().toInt(), currentButton->onCommand->text);
|
||||
}
|
||||
|
||||
|
||||
|
@ -143,6 +151,8 @@ void controllerSetup::offEventIndexChanged(int index) {
|
|||
QMutexLocker locker(mutex);
|
||||
currentButton->offCommand = &commands->at(offEvent->currentData().toInt());
|
||||
currentButton->offText->setPlainText(currentButton->offCommand->text);
|
||||
currentButton->offText->setPos(currentButton->pos.center().x() - currentButton->offText->boundingRect().width() / 2,
|
||||
(currentButton->pos.center().y() - currentButton->offText->boundingRect().height() / 2)+6);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +162,9 @@ void controllerSetup::knobEventIndexChanged(int index) {
|
|||
QMutexLocker locker(mutex);
|
||||
currentKnob->command = &commands->at(knobEvent->currentData().toInt());
|
||||
currentKnob->text->setPlainText(currentKnob->command->text);
|
||||
currentKnob->text->setPos(currentKnob->pos.center().x() - currentKnob->text->boundingRect().width() / 2,
|
||||
(currentKnob->pos.center().y() - currentKnob->text->boundingRect().height() / 2));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,11 +235,14 @@ void controllerSetup::newDevice(unsigned char devType, QVector<BUTTON>* but, QVe
|
|||
image.load(":/resources/ecoder.png");
|
||||
deviceName = "eCoderPlus";
|
||||
break;
|
||||
case QuickKeys:
|
||||
image.load(":/resources/quickkeys.png");
|
||||
deviceName = "QuickKeys";
|
||||
break;
|
||||
default:
|
||||
textItem->show();
|
||||
ui->graphicsView->setSceneRect(scene->itemsBoundingRect());
|
||||
this->adjustSize();
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -237,91 +253,123 @@ void controllerSetup::newDevice(unsigned char devType, QVector<BUTTON>* but, QVe
|
|||
ui->graphicsView->setMinimumSize(bgImage->boundingRect().width() + 100, bgImage->boundingRect().height() + 2);
|
||||
currentDevice = devType;
|
||||
|
||||
offEvent = new QComboBox;
|
||||
onEvent = new QComboBox;
|
||||
knobEvent = new QComboBox;
|
||||
|
||||
onEvent->blockSignals(true);
|
||||
offEvent->blockSignals(true);
|
||||
knobEvent->blockSignals(true);
|
||||
|
||||
onEvent->clear();
|
||||
offEvent->clear();
|
||||
knobEvent->clear();
|
||||
|
||||
onEvent->setMaxVisibleItems(5);
|
||||
offEvent->setMaxVisibleItems(5);
|
||||
knobEvent->setMaxVisibleItems(5);
|
||||
|
||||
onEvent->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
offEvent->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
knobEvent->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
|
||||
onEvent->setStyleSheet("combobox-popup: 0;");
|
||||
offEvent->setStyleSheet("combobox-popup: 0;");
|
||||
knobEvent->setStyleSheet("combobox-popup: 0;");
|
||||
|
||||
for (COMMAND& c : *commands) {
|
||||
if (c.cmdType == commandButton || c.text == "None") {
|
||||
onEvent->addItem(c.text, c.index);
|
||||
offEvent->addItem(c.text, c.index);
|
||||
}
|
||||
|
||||
if (c.cmdType == commandKnob || c.text == "None") {
|
||||
knobEvent->addItem(c.text, c.index);
|
||||
}
|
||||
if (currentDevice == QuickKeys) {
|
||||
ui->qkBrightCombo->setVisible(true);
|
||||
ui->qkOrientCombo->setVisible(true);
|
||||
ui->qkSpeedCombo->setVisible(true);
|
||||
ui->qkColorButton->setVisible(true);
|
||||
ui->qkTimeoutSpin->setVisible(true);
|
||||
ui->qkTimeoutLabel->setVisible(true);
|
||||
}
|
||||
|
||||
onEvent->blockSignals(false);
|
||||
offEvent->blockSignals(false);
|
||||
knobEvent->blockSignals(false);
|
||||
|
||||
onEvent->hide();
|
||||
offEvent->hide();
|
||||
knobEvent->hide();
|
||||
|
||||
// Set button text
|
||||
for (BUTTON& b : *buttons)
|
||||
else
|
||||
{
|
||||
|
||||
if (b.dev == currentDevice) {
|
||||
b.onText = new QGraphicsTextItem(b.onCommand->text);
|
||||
b.onText->setDefaultTextColor(b.textColour);
|
||||
scene->addItem(b.onText);
|
||||
b.onText->setPos(b.pos.center().x() - b.onText->boundingRect().width() / 2, b.pos.y());
|
||||
emit programButton(b.num, b.onCommand->text); // Program the button with ontext if supported
|
||||
|
||||
b.offText = new QGraphicsTextItem(b.offCommand->text);
|
||||
b.offText->setDefaultTextColor(b.textColour);
|
||||
scene->addItem(b.offText);
|
||||
b.offText->setPos(b.pos.center().x() - b.offText->boundingRect().width() / 2, b.pos.y() + 15);
|
||||
}
|
||||
ui->qkBrightCombo->setVisible(false);
|
||||
ui->qkOrientCombo->setVisible(false);
|
||||
ui->qkSpeedCombo->setVisible(false);
|
||||
ui->qkColorButton->setVisible(false);
|
||||
ui->qkTimeoutSpin->setVisible(false);
|
||||
ui->qkTimeoutLabel->setVisible(false);
|
||||
}
|
||||
|
||||
// Set knob text
|
||||
|
||||
for (KNOB& k : *knobs)
|
||||
if (currentDevice != usbNone)
|
||||
{
|
||||
if (k.dev == currentDevice) {
|
||||
k.text = new QGraphicsTextItem(k.command->text);
|
||||
k.text->setDefaultTextColor(k.textColour);
|
||||
scene->addItem(k.text);
|
||||
k.text->setPos(k.pos.center().x() - k.text->boundingRect().width() / 2, k.pos.y());
|
||||
offEvent = new QComboBox;
|
||||
onEvent = new QComboBox;
|
||||
knobEvent = new QComboBox;
|
||||
|
||||
onEvent->blockSignals(true);
|
||||
offEvent->blockSignals(true);
|
||||
knobEvent->blockSignals(true);
|
||||
|
||||
onEvent->clear();
|
||||
offEvent->clear();
|
||||
knobEvent->clear();
|
||||
|
||||
onEvent->setMaxVisibleItems(5);
|
||||
offEvent->setMaxVisibleItems(5);
|
||||
knobEvent->setMaxVisibleItems(5);
|
||||
|
||||
onEvent->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
offEvent->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
knobEvent->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
|
||||
onEvent->setStyleSheet("combobox-popup: 0;");
|
||||
offEvent->setStyleSheet("combobox-popup: 0;");
|
||||
knobEvent->setStyleSheet("combobox-popup: 0;");
|
||||
|
||||
for (COMMAND& c : *commands) {
|
||||
if (c.cmdType == commandButton || c.text == "None") {
|
||||
onEvent->addItem(c.text, c.index);
|
||||
offEvent->addItem(c.text, c.index);
|
||||
}
|
||||
|
||||
if (c.cmdType == commandKnob || c.text == "None") {
|
||||
knobEvent->addItem(c.text, c.index);
|
||||
}
|
||||
}
|
||||
|
||||
onEvent->blockSignals(false);
|
||||
offEvent->blockSignals(false);
|
||||
knobEvent->blockSignals(false);
|
||||
|
||||
onEvent->hide();
|
||||
offEvent->hide();
|
||||
knobEvent->hide();
|
||||
|
||||
// Set button text
|
||||
for (BUTTON& b : *buttons)
|
||||
{
|
||||
|
||||
if (b.dev == currentDevice) {
|
||||
b.onText = new QGraphicsTextItem(b.onCommand->text);
|
||||
b.onText->setDefaultTextColor(b.textColour);
|
||||
scene->addItem(b.onText);
|
||||
b.onText->setPos(b.pos.center().x() - b.onText->boundingRect().width() / 2,
|
||||
(b.pos.center().y() - b.onText->boundingRect().height() / 2) - 6);
|
||||
emit programButton(b.num, b.onCommand->text); // Program the button with ontext if supported
|
||||
|
||||
b.offText = new QGraphicsTextItem(b.offCommand->text);
|
||||
b.offText->setDefaultTextColor(b.textColour);
|
||||
scene->addItem(b.offText);
|
||||
b.offText->setPos(b.pos.center().x() - b.offText->boundingRect().width() / 2,
|
||||
(b.pos.center().y() - b.onText->boundingRect().height() / 2) + 6);
|
||||
}
|
||||
}
|
||||
|
||||
// Set knob text
|
||||
|
||||
for (KNOB& k : *knobs)
|
||||
{
|
||||
if (k.dev == currentDevice) {
|
||||
k.text = new QGraphicsTextItem(k.command->text);
|
||||
k.text->setDefaultTextColor(k.textColour);
|
||||
scene->addItem(k.text);
|
||||
k.text->setPos(k.pos.center().x() - k.text->boundingRect().width() / 2,
|
||||
(k.pos.center().y() - k.text->boundingRect().height() / 2));
|
||||
}
|
||||
}
|
||||
ui->graphicsView->setSceneRect(scene->itemsBoundingRect());
|
||||
ui->graphicsView->resize(ui->graphicsView->sizeHint());
|
||||
//this->resize(this->sizeHint());
|
||||
this->adjustSize();
|
||||
|
||||
// Add comboboxes to scene after everything else.
|
||||
offEventProxy = scene->addWidget(offEvent);
|
||||
connect(offEvent, SIGNAL(currentIndexChanged(int)), this, SLOT(offEventIndexChanged(int)));
|
||||
onEventProxy = scene->addWidget(onEvent);
|
||||
connect(onEvent, SIGNAL(currentIndexChanged(int)), this, SLOT(onEventIndexChanged(int)));
|
||||
knobEventProxy = scene->addWidget(knobEvent);
|
||||
connect(knobEvent, SIGNAL(currentIndexChanged(int)), this, SLOT(knobEventIndexChanged(int)));
|
||||
|
||||
if (currentDevice == QuickKeys) {
|
||||
// Finally update the device with the default values
|
||||
emit programBrightness((quint8)ui->qkBrightCombo->currentIndex() - 1);
|
||||
emit programOrientation((quint8)ui->qkOrientCombo->currentIndex());
|
||||
emit programSpeed((quint8)ui->qkSpeedCombo->currentIndex());
|
||||
emit programTimeout((quint8)ui->qkTimeoutSpin->value());
|
||||
emit programWheelColour((quint8)initialColor.red(), (quint8)initialColor.green(), (quint8)initialColor.blue());
|
||||
}
|
||||
}
|
||||
ui->graphicsView->setSceneRect(scene->itemsBoundingRect());
|
||||
ui->graphicsView->resize(ui->graphicsView->sizeHint());
|
||||
//this->resize(this->sizeHint());
|
||||
this->adjustSize();
|
||||
|
||||
// Add comboboxes to scene after everything else.
|
||||
offEventProxy = scene->addWidget(offEvent);
|
||||
connect(offEvent, SIGNAL(currentIndexChanged(int)), this, SLOT(offEventIndexChanged(int)));
|
||||
onEventProxy = scene->addWidget(onEvent);
|
||||
connect(onEvent, SIGNAL(currentIndexChanged(int)), this, SLOT(onEventIndexChanged(int)));
|
||||
knobEventProxy = scene->addWidget(knobEvent);
|
||||
connect(knobEvent, SIGNAL(currentIndexChanged(int)), this, SLOT(knobEventIndexChanged(int)));
|
||||
|
||||
}
|
||||
|
||||
void controllerSetup::receiveSensitivity(int val)
|
||||
|
@ -335,3 +383,78 @@ void controllerSetup::on_sensitivitySlider_valueChanged(int val)
|
|||
{
|
||||
emit sendSensitivity(val);
|
||||
}
|
||||
|
||||
void controllerSetup::on_qkBrightCombo_currentIndexChanged(int index)
|
||||
{
|
||||
if (index) {
|
||||
emit programBrightness((quint8)index - 1);
|
||||
}
|
||||
emit updateSettings((quint8)ui->qkBrightCombo->currentIndex(), (quint8)ui->qkOrientCombo->currentIndex(),
|
||||
(quint8)ui->qkSpeedCombo->currentIndex(), (quint8)ui->qkTimeoutSpin->value(), initialColor);
|
||||
}
|
||||
|
||||
void controllerSetup::on_qkOrientCombo_currentIndexChanged(int index)
|
||||
{
|
||||
if (index) {
|
||||
emit programOrientation((quint8)index);
|
||||
emit programOverlay(3, QString("Orientation set to %0").arg(ui->qkOrientCombo->currentText()));
|
||||
}
|
||||
emit updateSettings((quint8)ui->qkBrightCombo->currentIndex(), (quint8)ui->qkOrientCombo->currentIndex(),
|
||||
(quint8)ui->qkSpeedCombo->currentIndex(), (quint8)ui->qkTimeoutSpin->value(), initialColor);
|
||||
}
|
||||
|
||||
void controllerSetup::on_qkSpeedCombo_currentIndexChanged(int index)
|
||||
{
|
||||
if (index) {
|
||||
emit programSpeed((quint8)index);
|
||||
emit programOverlay(3, QString("Dial speed set to %0").arg(ui->qkSpeedCombo->currentText()));
|
||||
}
|
||||
emit updateSettings((quint8)ui->qkBrightCombo->currentIndex(), (quint8)ui->qkOrientCombo->currentIndex(),
|
||||
(quint8)ui->qkSpeedCombo->currentIndex(), (quint8)ui->qkTimeoutSpin->value(), initialColor);
|
||||
}
|
||||
|
||||
void controllerSetup::on_qkColorButton_clicked()
|
||||
{
|
||||
|
||||
QColorDialog::ColorDialogOptions options;
|
||||
options.setFlag(QColorDialog::ShowAlphaChannel, false);
|
||||
options.setFlag(QColorDialog::DontUseNativeDialog, false);
|
||||
QColor selColor = QColorDialog::getColor(initialColor, this, "Select Color", options);
|
||||
|
||||
if(!selColor.isValid())
|
||||
{
|
||||
selColor = initialColor;
|
||||
}
|
||||
initialColor = selColor;
|
||||
emit programWheelColour((quint8)selColor.red(), (quint8)selColor.green(), (quint8)initialColor.blue());
|
||||
emit updateSettings((quint8)ui->qkBrightCombo->currentIndex(), (quint8)ui->qkOrientCombo->currentIndex(),
|
||||
(quint8)ui->qkSpeedCombo->currentIndex(), (quint8)ui->qkTimeoutSpin->value(), initialColor);
|
||||
}
|
||||
|
||||
void controllerSetup::on_qkTimeoutSpin_valueChanged(int arg1)
|
||||
{
|
||||
emit programTimeout((quint8)arg1);
|
||||
emit programOverlay(3, QString("Sleep timeout set to %0 minutes").arg(arg1));
|
||||
emit updateSettings((quint8)ui->qkBrightCombo->currentIndex(), (quint8)ui->qkOrientCombo->currentIndex(),
|
||||
(quint8)ui->qkSpeedCombo->currentIndex(), (quint8)ui->qkTimeoutSpin->value(), initialColor);
|
||||
}
|
||||
|
||||
void controllerSetup::setDefaults(quint8 bright, quint8 orient, quint8 speed, quint8 timeout, QColor color)
|
||||
{
|
||||
ui->qkBrightCombo->blockSignals(true);
|
||||
ui->qkSpeedCombo->blockSignals(true);
|
||||
ui->qkOrientCombo->blockSignals(true);
|
||||
ui->qkTimeoutSpin->blockSignals(true);
|
||||
|
||||
ui->qkBrightCombo->setCurrentIndex((int)bright);
|
||||
ui->qkOrientCombo->setCurrentIndex((int)orient);
|
||||
ui->qkSpeedCombo->setCurrentIndex((int)speed);
|
||||
ui->qkTimeoutSpin->setValue((int)timeout);
|
||||
initialColor = color;
|
||||
|
||||
ui->qkBrightCombo->blockSignals(false);
|
||||
ui->qkSpeedCombo->blockSignals(false);
|
||||
ui->qkOrientCombo->blockSignals(false);
|
||||
ui->qkTimeoutSpin->blockSignals(false);
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <QDebug>
|
||||
#include <QObject>
|
||||
#include <QColorDialog>
|
||||
|
||||
#include "usbcontroller.h"
|
||||
|
||||
|
@ -36,7 +37,14 @@ public:
|
|||
|
||||
signals:
|
||||
void sendSensitivity(int val);
|
||||
void programButton(int but, QString text);
|
||||
void programButton(quint8 but, QString text);
|
||||
void programBrightness(quint8 level);
|
||||
void programWheelColour(quint8 r, quint8 g, quint8 b);
|
||||
void programOverlay(quint8 duration, QString text);
|
||||
void programOrientation(quint8 value);
|
||||
void programSpeed(quint8 value);
|
||||
void programTimeout(quint8 value);
|
||||
void updateSettings(quint8 bright, quint8 orient, quint8 speed, quint8 timeout, QColor color);
|
||||
|
||||
public slots:
|
||||
void newDevice(unsigned char devType, QVector<BUTTON>* but, QVector<KNOB>* kb, QVector<COMMAND>* cmd, QMutex* mut);
|
||||
|
@ -46,6 +54,12 @@ public slots:
|
|||
void knobEventIndexChanged(int index);
|
||||
void receiveSensitivity(int val);
|
||||
void on_sensitivitySlider_valueChanged(int val);
|
||||
void on_qkBrightCombo_currentIndexChanged(int index);
|
||||
void on_qkOrientCombo_currentIndexChanged(int index);
|
||||
void on_qkSpeedCombo_currentIndexChanged(int index);
|
||||
void on_qkColorButton_clicked();
|
||||
void on_qkTimeoutSpin_valueChanged(int arg1);
|
||||
void setDefaults(quint8 bright, quint8 orient, quint8 speed, quint8 timeout, QColor color);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -64,11 +78,14 @@ private:
|
|||
QComboBox* onEvent = Q_NULLPTR;
|
||||
QComboBox* offEvent = Q_NULLPTR;
|
||||
QComboBox* knobEvent = Q_NULLPTR;
|
||||
QComboBox* qkBright = Q_NULLPTR;
|
||||
QGraphicsProxyWidget* onEventProxy = Q_NULLPTR;
|
||||
QGraphicsProxyWidget* offEventProxy = Q_NULLPTR;
|
||||
QGraphicsProxyWidget* knobEventProxy = Q_NULLPTR;
|
||||
QGraphicsProxyWidget* qkBrightProxy = Q_NULLPTR;
|
||||
QString deviceName;
|
||||
QMutex* mutex;
|
||||
QColor initialColor = Qt::white;
|
||||
};
|
||||
|
||||
|
||||
|
@ -92,17 +109,6 @@ protected:
|
|||
QGraphicsScene::mousePressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
|
||||
|
||||
}
|
||||
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>302</width>
|
||||
<width>442</width>
|
||||
<height>343</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -14,24 +14,7 @@
|
|||
<string>Controller setup</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><span style=" font-weight:700;">Button configuration: </span>Right-click on each button to configure it.</p><p align="center">Top selection is command to send when button is pressed and bottom is (optional) command to send when button is released.</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="7" column="0">
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
|
@ -77,7 +60,134 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QComboBox" name="qkBrightCombo">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Brightness</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Off</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Low</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Medium</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>High</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="qkSpeedCombo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Speed</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Fastest</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Faster</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Normal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Slower</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Slowest</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="qkOrientCombo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Orientation</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rotate 0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rotate 90</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rotate 180</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rotate 270</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="qkColorButton">
|
||||
<property name="text">
|
||||
<string>Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="qkTimeoutLabel">
|
||||
<property name="text">
|
||||
<string>Timeout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="qkTimeoutSpin">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
|
@ -104,6 +214,23 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"><span style=" font-weight:700;">Button configuration: </span>Right-click on each button to configure it.</p><p align="center">Top selection is command to send when button is pressed and bottom is (optional) command to send when button is released.</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
|
|
9
prefs.h
9
prefs.h
|
@ -47,6 +47,11 @@ struct preferences {
|
|||
bool automaticSidebandSwitching = true;
|
||||
bool enableUSBControllers;
|
||||
int usbSensitivity;
|
||||
quint8 usbSpeed;
|
||||
quint8 usbTimeout;
|
||||
quint8 usbBrightness;
|
||||
quint8 usbOrientation;
|
||||
QColor usbColor;
|
||||
|
||||
// LAN:
|
||||
bool enableLAN;
|
||||
|
@ -63,8 +68,8 @@ struct preferences {
|
|||
QString clusterTcpServerName;
|
||||
QString clusterTcpUserName;
|
||||
QString clusterTcpPassword;
|
||||
int clusterTimeout; // used?
|
||||
bool clusterSkimmerSpotsEnable; // where is this used?
|
||||
int clusterTimeout;
|
||||
bool clusterSkimmerSpotsEnable;
|
||||
};
|
||||
|
||||
#endif // PREFS_H
|
||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 113 KiB |
|
@ -5,6 +5,7 @@
|
|||
<file>shuttlepro.png</file>
|
||||
<file>rc28.png</file>
|
||||
<file>ecoder.png</file>
|
||||
<file>quickkeys.png</file>
|
||||
<file>xbox.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
usbController::usbController()
|
||||
{
|
||||
// As this is run in it's own thread, don't do anything in the constructor
|
||||
qInfo(logUsbControl()) << "Starting usbController()";
|
||||
}
|
||||
|
||||
|
@ -20,6 +21,7 @@ usbController::~usbController()
|
|||
qInfo(logUsbControl) << "Ending usbController()";
|
||||
|
||||
if (handle) {
|
||||
programOverlay(60, "Goodbye from wfview");
|
||||
|
||||
if (usbDevice == RC28) {
|
||||
ledControl(false, 3);
|
||||
|
@ -76,7 +78,10 @@ void usbController::init(int sens, QMutex* mut)
|
|||
struct hid_device_info* devs;
|
||||
devs = hid_enumerate(0x0, 0x0);
|
||||
while (devs) {
|
||||
qInfo(logUsbControl()) << QString("Device found: %0 manufacturer: %1 usage: 0x%3 usage_page 0x%4")
|
||||
qInfo(logUsbControl()) << QString("Device found: (%0:%1) %2 manufacturer: (%3)%4 usage: 0x%5 usage_page 0x%6")
|
||||
.arg(devs->vendor_id, 4, 16, QChar('0'))
|
||||
.arg(devs->product_id, 4, 16, QChar('0'))
|
||||
.arg(QString::fromWCharArray(devs->product_string))
|
||||
.arg(QString::fromWCharArray(devs->product_string))
|
||||
.arg(QString::fromWCharArray(devs->manufacturer_string))
|
||||
.arg(devs->usage, 4, 16, QChar('0'))
|
||||
|
@ -88,42 +93,7 @@ void usbController::init(int sens, QMutex* mut)
|
|||
}
|
||||
}
|
||||
|
||||
void usbController::receiveCommands(QVector<COMMAND>* cmds)
|
||||
{
|
||||
qDebug(logUsbControl()) << "Receiving commands";
|
||||
commands = cmds;
|
||||
}
|
||||
|
||||
void usbController::receiveButtons(QVector<BUTTON>* buts)
|
||||
{
|
||||
qDebug(logUsbControl()) << "Receiving buttons";
|
||||
buttonList = buts;
|
||||
}
|
||||
|
||||
void usbController::receiveKnobs(QVector<KNOB>* kbs)
|
||||
{
|
||||
qDebug(logUsbControl()) << "Receiving knobs";
|
||||
knobList = kbs;
|
||||
}
|
||||
|
||||
void usbController::programButton(int val, QString text)
|
||||
{
|
||||
if (usbDevice == QuickKeys) {
|
||||
QByteArray data(16, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb1;
|
||||
data[2] = val;
|
||||
data.replace(3, text.mid(0, 8).length(), text.mid(0, 8).toLocal8Bit());
|
||||
|
||||
int res = hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
|
||||
if (res < 0) {
|
||||
qDebug(logUsbControl()) << "Unable to write(), Error:" << hid_error(this->handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* run() is called every 2s and attempts to connect to a supported controller */
|
||||
void usbController::run()
|
||||
{
|
||||
if (commands == Q_NULLPTR || hidStatus) {
|
||||
|
@ -248,12 +218,18 @@ void usbController::run()
|
|||
|
||||
while (devs) {
|
||||
for (int i = 0; i < (int)sizeof knownUsbDevices / (int)sizeof knownUsbDevices[0]; i++) {
|
||||
if (devs->vendor_id == knownUsbDevices[i][1] && devs->product_id == knownUsbDevices[i][2]) {
|
||||
if (devs->vendor_id == knownUsbDevices[i][1] && devs->product_id == knownUsbDevices[i][2]
|
||||
&& (knownUsbDevices[i][3] == 0x00 || devs->usage == knownUsbDevices[i][3])
|
||||
&& (knownUsbDevices[i][4] == 0x00 || devs->usage_page == knownUsbDevices[i][4]))
|
||||
{
|
||||
this->manufacturer = QString::fromWCharArray(devs->manufacturer_string);
|
||||
this->vendorId = devs->vendor_id;
|
||||
this->productId = devs->product_id;
|
||||
this->product = QString::fromWCharArray(devs->product_string);
|
||||
this->serial = QString::fromWCharArray(devs->serial_number);
|
||||
usbDevice = (usbDeviceType)knownUsbDevices[i][0];
|
||||
this->path = QString::fromLocal8Bit(devs->path);
|
||||
this->deviceId = QString("0x%1").arg(this->productId, 4, 16, QChar('0'));
|
||||
this->usbDevice = (usbDeviceType)knownUsbDevices[i][0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +264,22 @@ void usbController::run()
|
|||
knobSend.append({ 0,0,0 });
|
||||
}
|
||||
else if (usbDevice == QuickKeys) {
|
||||
// Subscribe to event streams
|
||||
|
||||
QByteArray b(32,0x0);
|
||||
b[0] = (qint8)0x02;
|
||||
b[1] = (qint8)0xb0;
|
||||
b[2] = (qint8)0x04;
|
||||
|
||||
b.replace(10, sizeof(this->deviceId), this->deviceId.toLocal8Bit());
|
||||
hid_write(this->handle, (const unsigned char*)b.constData(), b.size());
|
||||
|
||||
b[0] = (qint8)0x02;
|
||||
b[1] = (qint8)0xb4;
|
||||
b[2] = (qint8)0x10;
|
||||
b.replace(10, sizeof(this->deviceId), this->deviceId.toLocal8Bit());
|
||||
|
||||
hid_write(this->handle, (const unsigned char*)b.constData(), b.size());
|
||||
}
|
||||
|
||||
|
||||
|
@ -313,6 +304,7 @@ void usbController::run()
|
|||
|
||||
}
|
||||
|
||||
/* runTimer is called every 25ms once a connection to a supported controller is established */
|
||||
void usbController::runTimer()
|
||||
{
|
||||
int res=1;
|
||||
|
@ -392,12 +384,12 @@ void usbController::runTimer()
|
|||
auto but = std::find_if(buttonList->begin(), buttonList->end(), [this, i](const BUTTON& b)
|
||||
{ return (b.dev == this->usbDevice && b.num == i); });
|
||||
if (but != buttonList->end()) {
|
||||
if ((tempButtons >> i & 1) && !(buttons >> i & 1) && but->onCommand->index > 0)
|
||||
if ((tempButtons >> i & 1) && !(buttons >> i & 1))
|
||||
{
|
||||
qDebug(logUsbControl()) << "On Button event:" << but->onCommand->text;
|
||||
emit button(but->onCommand);
|
||||
}
|
||||
else if ((buttons >> i & 1) && !(tempButtons >> i & 1) && but->offCommand->index > 0)
|
||||
else if ((buttons >> i & 1) && !(tempButtons >> i & 1))
|
||||
{
|
||||
qDebug(logUsbControl()) << "Off Button event:" << but->offCommand->text;
|
||||
emit button(but->offCommand);
|
||||
|
@ -550,15 +542,15 @@ void usbController::runTimer()
|
|||
// Step through all buttons and emit ones that have been pressed.
|
||||
for (unsigned char i = 1; i < 23; i++)
|
||||
{
|
||||
auto but = std::find_if(buttonList->begin(), buttonList->end(), [this, i](const BUTTON& b)
|
||||
{ return (b.dev == this->usbDevice && b.num == i); });
|
||||
auto but = std::find_if(buttonList->begin(), buttonList->end(), [this, i](const BUTTON& b)
|
||||
{ return (b.dev == this->usbDevice && b.num == i); });
|
||||
if (but != buttonList->end()) {
|
||||
if ((tempButtons >> i & 1) && !(buttons >> i & 1) && but->onCommand->index > 0)
|
||||
if ((tempButtons >> i & 1) && !(buttons >> i & 1))
|
||||
{
|
||||
qDebug(logUsbControl()) << "On Button event:" << but->onCommand->text;
|
||||
emit button(but->onCommand);
|
||||
}
|
||||
else if ((buttons >> i & 1) && !(tempButtons >> i & 1) && but->offCommand->index > 0)
|
||||
else if ((buttons >> i & 1) && !(tempButtons >> i & 1))
|
||||
{
|
||||
qDebug(logUsbControl()) << "Off Button event:" << but->offCommand->text;
|
||||
emit button(but->offCommand);
|
||||
|
@ -581,6 +573,50 @@ void usbController::runTimer()
|
|||
// Tuning knob
|
||||
jogCounter = jogCounter + (qint8)data[13];
|
||||
|
||||
} else if (usbDevice == QuickKeys && (quint8)data[0] == 0x02) {
|
||||
|
||||
if ((quint8)data[1] == 0xf0) {
|
||||
|
||||
qInfo(logUsbControl()) << "Received:" << data;
|
||||
quint32 tempButtons = data[3] << 8 | data[2] & 0xff;
|
||||
|
||||
// Step through all buttons and emit ones that have been pressed.
|
||||
for (unsigned char i = 0; i < 10; i++)
|
||||
{
|
||||
auto but = std::find_if(buttonList->begin(), buttonList->end(), [this, i](const BUTTON& b)
|
||||
{ return (b.dev == this->usbDevice && b.num == i); });
|
||||
if (but != buttonList->end()) {
|
||||
if ((tempButtons >> i & 1) && !(buttons >> i & 1))
|
||||
{
|
||||
qDebug(logUsbControl()) << "On Button event:" << but->onCommand->text;
|
||||
emit button(but->onCommand);
|
||||
programButton(but->num, but->offCommand->text);
|
||||
}
|
||||
else if ((buttons >> i & 1) && !(tempButtons >> i & 1))
|
||||
{
|
||||
qDebug(logUsbControl()) << "Off Button event:" << but->offCommand->text;
|
||||
emit button(but->offCommand);
|
||||
programButton(but->num, but->onCommand->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buttons = tempButtons;
|
||||
|
||||
// Tuning knob
|
||||
if (data[7] & 0x01) {
|
||||
jogCounter++;
|
||||
}
|
||||
else if (data[7] & 0x02) {
|
||||
jogCounter--;
|
||||
}
|
||||
}
|
||||
else if ((quint8)data[1] == 0xf2 && (quint8)data[2] == 0x01)
|
||||
{
|
||||
// Battery level
|
||||
quint8 battery = (quint8)data[3];
|
||||
qDebug(logUsbControl()) << QString("Battery level %1 %").arg(battery);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastusbController.msecsTo(QTime::currentTime()) >= 100 || lastusbController > QTime::currentTime())
|
||||
|
@ -642,6 +678,48 @@ void usbController::runTimer()
|
|||
QTimer::singleShot(25, this, SLOT(runTimer()));
|
||||
}
|
||||
|
||||
|
||||
/* Functions below receive various settings from other classes/threads */
|
||||
void usbController::receiveCommands(QVector<COMMAND>* cmds)
|
||||
{
|
||||
qDebug(logUsbControl()) << "Receiving commands";
|
||||
commands = cmds;
|
||||
}
|
||||
|
||||
void usbController::receiveButtons(QVector<BUTTON>* buts)
|
||||
{
|
||||
qDebug(logUsbControl()) << "Receiving buttons";
|
||||
buttonList = buts;
|
||||
}
|
||||
|
||||
void usbController::receiveKnobs(QVector<KNOB>* kbs)
|
||||
{
|
||||
qDebug(logUsbControl()) << "Receiving knobs";
|
||||
knobList = kbs;
|
||||
}
|
||||
|
||||
void usbController::receiveSensitivity(int val)
|
||||
{
|
||||
sensitivity = val;
|
||||
}
|
||||
|
||||
void usbController::receivePTTStatus(bool on) {
|
||||
static QColor lastColour = currentColour;
|
||||
if (on) {
|
||||
lastColour = currentColour;
|
||||
programWheelColour(255, 0, 0);
|
||||
}
|
||||
else {
|
||||
programWheelColour((quint8)lastColour.red(), (quint8)lastColour.green(), (quint8)lastColour.blue());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* All functions below here are for specific controllers
|
||||
*/
|
||||
|
||||
/* Functions below are for RC28 */
|
||||
|
||||
void usbController::ledControl(bool on, unsigned char num)
|
||||
{
|
||||
if (usbDevice == RC28) {
|
||||
|
@ -677,6 +755,10 @@ void usbController::getVersion()
|
|||
qDebug(logUsbControl()) << "Unable to write(), Error:" << hid_error(handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* End of RC28 functions*/
|
||||
|
||||
/* Functions below are for Gamepad controllers */
|
||||
void usbController::buttonState(QString name, bool val)
|
||||
{
|
||||
for (BUTTON* but = buttonList->begin(); but != buttonList->end(); but++) {
|
||||
|
@ -693,6 +775,7 @@ void usbController::buttonState(QString name, bool val)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usbController::buttonState(QString name, double val)
|
||||
{
|
||||
|
||||
|
@ -718,9 +801,133 @@ void usbController::buttonState(QString name, double val)
|
|||
*/
|
||||
}
|
||||
|
||||
void usbController::receiveSensitivity(int val)
|
||||
/* End of Gamepad functions*/
|
||||
|
||||
|
||||
/* Functions below are for Xencelabs QuickKeys*/
|
||||
void usbController::programButton(quint8 val, QString text)
|
||||
{
|
||||
sensitivity = val;
|
||||
if (handle && usbDevice == QuickKeys && val < 8) {
|
||||
text = text.mid(0, 10); // Make sure text is no more than 10 characters.
|
||||
qDebug(logUsbControl()) << QString("Programming button %0 with %1").arg(val).arg(text);
|
||||
QByteArray data(32, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb1;
|
||||
data[3] = val + 1;
|
||||
data[5] = text.length() * 2;
|
||||
data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
|
||||
|
||||
QByteArray le = qToLittleEndian(QByteArray::fromRawData(reinterpret_cast<const char*>(text.constData()), text.size() * 2));
|
||||
data.replace(16, le.size(), le);
|
||||
|
||||
int res = hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
|
||||
if (res < 0) {
|
||||
qDebug(logUsbControl()) << "Unable to write(), Error:" << hid_error(this->handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void usbController::programBrightness(quint8 val) {
|
||||
if (handle && usbDevice == QuickKeys) {
|
||||
qDebug(logUsbControl()) << QString("Programming brightness to %0").arg(val);
|
||||
QByteArray data(32, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb1;
|
||||
data[2] = (qint8)0x0a;
|
||||
data[3] = (qint8)0x01;
|
||||
data[4] = val;
|
||||
data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
|
||||
hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
}
|
||||
}
|
||||
|
||||
void usbController::programOrientation(quint8 val) {
|
||||
if (handle && usbDevice == QuickKeys) {
|
||||
qDebug(logUsbControl()) << QString("Programming orientation to %0").arg(val);
|
||||
QByteArray data(32, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb1;
|
||||
data[2] = (qint8)val;
|
||||
data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
|
||||
hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
}
|
||||
}
|
||||
|
||||
void usbController::programSpeed(quint8 val) {
|
||||
if (handle && usbDevice == QuickKeys) {
|
||||
qDebug(logUsbControl()) << QString("Programming speed to %0").arg(val);
|
||||
QByteArray data(32, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb4;
|
||||
data[2] = (qint8)0x04;
|
||||
data[3] = (qint8)0x01;
|
||||
data[4] = (qint8)0x01;
|
||||
data[5] = val;
|
||||
data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
|
||||
hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void usbController::programWheelColour(quint8 r, quint8 g, quint8 b)
|
||||
{
|
||||
if (handle && usbDevice == QuickKeys) {
|
||||
QByteArray data(32, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb4;
|
||||
data[2] = (qint8)0x01;
|
||||
data[3] = (qint8)0x01;
|
||||
data[6] = (qint8)r;
|
||||
data[7] = (qint8)g;
|
||||
data[8] = (qint8)b;
|
||||
data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
|
||||
hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
currentColour.setRed(r);
|
||||
currentColour.setGreen(g);
|
||||
currentColour.setBlue(b);
|
||||
}
|
||||
}
|
||||
|
||||
void usbController::programOverlay(quint8 duration, QString text)
|
||||
{
|
||||
if (handle && usbDevice == QuickKeys) {
|
||||
text = text.mid(0, 32);
|
||||
QByteArray data(32, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb1;
|
||||
data[3] = (qint8)duration;
|
||||
data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
|
||||
for (int i = 0; i < text.length(); i = i + 8)
|
||||
{
|
||||
data[2] = (i == 0) ? 0x05 : 0x06;
|
||||
QByteArray le = qToLittleEndian(QByteArray::fromRawData(reinterpret_cast<const char*>(text.mid(i, 8).constData()), text.mid(i, 8).size() * 2));
|
||||
data.replace(16, le.size(), le);
|
||||
data[5] = text.mid(i, 8).length() * 2;
|
||||
data[6] = (i > 0 && text.mid(i).size() > 8) ? 0x01 : 0x00;
|
||||
hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
}
|
||||
//qInfo(logUsbControl()) << "Sent overlay" << text;
|
||||
}
|
||||
}
|
||||
|
||||
void usbController::programTimeout(quint8 val)
|
||||
{
|
||||
if (handle && usbDevice == QuickKeys) {
|
||||
qInfo(logUsbControl()) << QString("Programming timeout to %0 minutes").arg(val);
|
||||
QByteArray data(32, 0x0);
|
||||
data[0] = (qint8)0x02;
|
||||
data[1] = (qint8)0xb4;
|
||||
data[2] = (qint8)0x08;
|
||||
data[3] = (qint8)0x01;
|
||||
data[4] = val;
|
||||
data.replace(10, this->deviceId.size(), this->deviceId.toLocal8Bit());
|
||||
hid_write(this->handle, (const unsigned char*)data.constData(), data.size());
|
||||
}
|
||||
}
|
||||
|
||||
/* End of functions for Xencelabs QuickKeys*/
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QIODevice>
|
||||
#include <QtEndian>
|
||||
|
||||
#if defined(USB_CONTROLLER) && QT_VERSION < QT_VERSION_CHECK(6,0,0)
|
||||
#include <QGamepad>
|
||||
|
@ -128,9 +130,16 @@ public slots:
|
|||
void receiveCommands(QVector<COMMAND>*);
|
||||
void receiveButtons(QVector<BUTTON>*);
|
||||
void receiveKnobs(QVector<KNOB>*);
|
||||
void receivePTTStatus(bool on);
|
||||
void getVersion();
|
||||
void receiveSensitivity(int val);
|
||||
void programButton(int val, QString text);
|
||||
void programButton(quint8 val, QString text);
|
||||
void programBrightness(quint8 val);
|
||||
void programOrientation(quint8 val);
|
||||
void programSpeed(quint8 val);
|
||||
void programWheelColour(quint8 r, quint8 g, quint8 b);
|
||||
void programOverlay(quint8 duration, QString text);
|
||||
void programTimeout(quint8 val);
|
||||
|
||||
signals:
|
||||
void jogPlus();
|
||||
|
@ -161,11 +170,15 @@ private:
|
|||
QString product="";
|
||||
QString manufacturer="";
|
||||
QString serial="<none>";
|
||||
QString deviceId = "";
|
||||
QString path = "";
|
||||
quint16 vendorId = 0;
|
||||
quint16 productId = 0;
|
||||
int sensitivity = 1;
|
||||
QList<int> knobValues;
|
||||
QList<quint8> knobSend;
|
||||
QMutex* mutex=Q_NULLPTR;
|
||||
QColor currentColour;
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
|
||||
QGamepad* gamepad=Q_NULLPTR;
|
||||
#endif
|
||||
|
@ -173,13 +186,14 @@ private:
|
|||
void buttonState(QString but, double val);
|
||||
usbDeviceType usbDevice = usbNone;
|
||||
|
||||
unsigned short knownUsbDevices[5][3] = {
|
||||
{shuttleXpress,0x0b33,0x0020},
|
||||
{shuttlePro2,0x0b33,0x0030},
|
||||
unsigned short knownUsbDevices[6][5] = {
|
||||
{shuttleXpress,0x0b33,0x0020,0x0001,0x000c},
|
||||
{shuttlePro2,0x0b33,0x0030,0x0001,0x000c},
|
||||
//{eCoderPlus,0x0c26,0x001e}, // Only enable for testing!
|
||||
{RC28,0x0c26,0x001e},
|
||||
{eCoderPlus, 0x1fc9, 0x0003},
|
||||
{QuickKeys, 0x28bd, 0x5202}
|
||||
{RC28,0x0c26,0x001e,0x0004,0x0004},
|
||||
{eCoderPlus, 0x1fc9, 0x0003,0x0000,0x0000},
|
||||
{QuickKeys, 0x28bd, 0x5202,0x0001,0xff0a},
|
||||
{QuickKeys, 0x28bd, 0x5203,0x0001,0xff0a}
|
||||
};
|
||||
|
||||
protected:
|
||||
|
|
136
wfmain.cpp
136
wfmain.cpp
|
@ -1680,8 +1680,18 @@ void wfmain::setupUsbControllerDevice()
|
|||
connect(shut, SIGNAL(sendSensitivity(int)), usbControllerDev, SLOT(receiveSensitivity(int)));
|
||||
connect(shut, SIGNAL(sendSensitivity(int)), this, SLOT(receiveUsbSensitivity(int)));
|
||||
connect(usbControllerDev, SIGNAL(sendSensitivity(int)), shut, SLOT(receiveSensitivity(int)));
|
||||
connect(shut, SIGNAL(programButton(int, QString)), usbControllerDev, SLOT(programButton(int, QString)));
|
||||
connect(this, SIGNAL(initUsbController(int,QMutex*)), usbControllerDev, SLOT(init(int,QMutex*)));
|
||||
connect(shut, SIGNAL(programButton(quint8, QString)), usbControllerDev, SLOT(programButton(quint8, QString)));
|
||||
connect(shut, SIGNAL(programBrightness(quint8)), usbControllerDev, SLOT(programBrightness(quint8)));
|
||||
connect(shut, SIGNAL(programOrientation(quint8)), usbControllerDev, SLOT(programOrientation(quint8)));
|
||||
connect(shut, SIGNAL(programSpeed(quint8)), usbControllerDev, SLOT(programSpeed(quint8)));
|
||||
connect(shut, SIGNAL(programWheelColour(quint8, quint8, quint8)), usbControllerDev, SLOT(programWheelColour(quint8, quint8, quint8)));
|
||||
connect(shut, SIGNAL(programOverlay(quint8, QString)), usbControllerDev, SLOT(programOverlay(quint8, QString)));
|
||||
connect(shut, SIGNAL(programTimeout(quint8)), usbControllerDev, SLOT(programTimeout(quint8)));
|
||||
connect(shut, SIGNAL(updateSettings(quint8, quint8, quint8, quint8, QColor)), this, SLOT(receiveUsbSettings(quint8, quint8, quint8, quint8, QColor)));
|
||||
connect(this, SIGNAL(setPTT(bool)), usbControllerDev, SLOT(receivePTTStatus(bool)));
|
||||
connect(this, SIGNAL(initUsbController(int, QMutex*)), usbControllerDev, SLOT(init(int, QMutex*)));
|
||||
connect(this, SIGNAL(initUsbDefaults(quint8, quint8, quint8, quint8, QColor)), shut, SLOT(setDefaults(quint8, quint8, quint8, quint8, QColor)));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1888,6 +1898,11 @@ void wfmain::setDefPrefs()
|
|||
defPrefs.audioSystem = qtAudio;
|
||||
defPrefs.enableUSBControllers = false;
|
||||
defPrefs.usbSensitivity = 1;
|
||||
defPrefs.usbSpeed = 3;
|
||||
defPrefs.usbTimeout = 30;
|
||||
defPrefs.usbBrightness = 3;
|
||||
defPrefs.usbOrientation = 3;
|
||||
defPrefs.usbColor = Qt::white;
|
||||
|
||||
udpDefPrefs.ipAddress = QString("");
|
||||
udpDefPrefs.controlLANPort = 50001;
|
||||
|
@ -2442,6 +2457,12 @@ void wfmain::loadSettings()
|
|||
ui->usbCommandsResetBtn->setEnabled(prefs.enableUSBControllers);
|
||||
ui->usbResetLbl->setVisible(prefs.enableUSBControllers);
|
||||
|
||||
prefs.usbBrightness = (quint8)settings->value("USBBrightness", defPrefs.usbBrightness).toInt();
|
||||
prefs.usbOrientation = (quint8)settings->value("USBOrientation", defPrefs.usbOrientation).toInt();
|
||||
prefs.usbSpeed = (quint8)settings->value("USBSpeed", defPrefs.usbSpeed).toInt();
|
||||
prefs.usbTimeout = (quint8)settings->value("USBBTimeout", defPrefs.usbTimeout).toInt();
|
||||
prefs.usbColor.setNamedColor(settings->value("USBColor", defPrefs.usbColor.name(QColor::HexArgb)).toString());
|
||||
|
||||
/*Ensure that no operations on the usb commands/buttons/knobs take place*/
|
||||
QMutexLocker locker(&usbMutex);
|
||||
|
||||
|
@ -2452,6 +2473,7 @@ void wfmain::loadSettings()
|
|||
emit sendUsbControllerCommands(&usbCommands);
|
||||
emit sendUsbControllerButtons(&usbButtons);
|
||||
emit sendUsbControllerKnobs(&usbKnobs);
|
||||
emit initUsbDefaults(prefs.usbBrightness, prefs.usbOrientation, prefs.usbSpeed, prefs.usbTimeout, prefs.usbColor);
|
||||
}
|
||||
|
||||
int numCommands = settings->beginReadArray("Commands");
|
||||
|
@ -2956,6 +2978,12 @@ void wfmain::saveSettings()
|
|||
// Store USB Controller
|
||||
settings->setValue("EnableUSBControllers", prefs.enableUSBControllers);
|
||||
settings->setValue("USBSensitivity", prefs.usbSensitivity);
|
||||
|
||||
settings->setValue("USBBrightness", prefs.usbBrightness);
|
||||
settings->setValue("USBOrientation", prefs.usbOrientation);
|
||||
settings->setValue("USBSpeed", prefs.usbSpeed);
|
||||
settings->setValue("USBTimeout", prefs.usbTimeout);
|
||||
settings->setValue("USBColor", prefs.usbColor.name(QColor::HexArgb));
|
||||
|
||||
QMutexLocker locker(&usbMutex);
|
||||
settings->beginWriteArray("Buttons");
|
||||
|
@ -8046,17 +8074,18 @@ QColor wfmain::getColorFromPicker(QColor initialColor)
|
|||
int alphaVal = 0;
|
||||
bool ok = false;
|
||||
|
||||
if(selColor.isValid())
|
||||
if (selColor.isValid())
|
||||
{
|
||||
if(selColor.alpha() == 0)
|
||||
if (selColor.alpha() == 0)
|
||||
{
|
||||
alphaVal = QInputDialog::getInt(this, tr("Specify Opacity"),
|
||||
tr("You specified an opacity value of 0. \nDo you want to change it? (0=transparent, 255=opaque)"), 0, 0, 255, 1,
|
||||
&ok);
|
||||
if(!ok)
|
||||
tr("You specified an opacity value of 0. \nDo you want to change it? (0=transparent, 255=opaque)"), 0, 0, 255, 1,
|
||||
&ok);
|
||||
if (!ok)
|
||||
{
|
||||
return selColor;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
selColor.setAlpha(alphaVal);
|
||||
return selColor;
|
||||
}
|
||||
|
@ -9125,6 +9154,7 @@ void wfmain::on_enableUsbChk_clicked(bool checked)
|
|||
emit sendUsbControllerCommands(&usbCommands);
|
||||
emit sendUsbControllerButtons(&usbButtons);
|
||||
emit sendUsbControllerKnobs(&usbKnobs);
|
||||
emit initUsbDefaults(prefs.usbBrightness, prefs.usbOrientation, prefs.usbSpeed, prefs.usbTimeout, prefs.usbColor);
|
||||
}
|
||||
else {
|
||||
if (shut != Q_NULLPTR) {
|
||||
|
@ -9339,6 +9369,18 @@ void wfmain::resetUsbButtons()
|
|||
usbButtons.append(BUTTON(eCoderPlus, 20, QRect(290, 2, 55, 30), Qt::red, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(eCoderPlus, 21, QRect(404, 2, 55, 30), Qt::red, &usbCommands[0], &usbCommands[0]));
|
||||
|
||||
// QuickKeys
|
||||
usbButtons.append(BUTTON(QuickKeys, 0, QRect(77, 204, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 1, QRect(77, 276, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 2, QRect(77, 348, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 3, QRect(77, 422, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 4, QRect(230, 204, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 5, QRect(230, 276, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 6, QRect(230, 348, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 7, QRect(230, 422, 39, 63), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 8, QRect(143, 515, 55, 40), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
usbButtons.append(BUTTON(QuickKeys, 9, QRect(139, 68, 65, 65), Qt::white, &usbCommands[0], &usbCommands[0]));
|
||||
|
||||
emit sendUsbControllerButtons(&usbButtons);
|
||||
|
||||
#endif
|
||||
|
@ -9375,42 +9417,42 @@ void wfmain::resetUsbCommands()
|
|||
usbCommands.append(COMMAND(num++, "Span-", commandButton, cmdSetSpanDown, 0x0));
|
||||
usbCommands.append(COMMAND(num++, "Mode+", commandButton, cmdSetModeUp, 0x0));
|
||||
usbCommands.append(COMMAND(num++, "Mode-", commandButton, cmdSetModeDown, 0x0));
|
||||
usbCommands.append(COMMAND(num++, "LSB", commandButton, cmdSetMode, modeLSB));
|
||||
usbCommands.append(COMMAND(num++, "USB", commandButton, cmdSetMode, modeUSB));
|
||||
usbCommands.append(COMMAND(num++, "LSBD", commandButton, cmdSetMode, modeLSB_D));
|
||||
usbCommands.append(COMMAND(num++, "USBD", commandButton, cmdSetMode, modeUSB_D));
|
||||
usbCommands.append(COMMAND(num++, "CW", commandButton, cmdSetMode, modeCW));
|
||||
usbCommands.append(COMMAND(num++, "CWR", commandButton, cmdSetMode, modeCW_R));
|
||||
usbCommands.append(COMMAND(num++, "FM", commandButton, cmdSetMode, modeFM));
|
||||
usbCommands.append(COMMAND(num++, "AM", commandButton, cmdSetMode, modeAM));
|
||||
usbCommands.append(COMMAND(num++, "RTTY", commandButton, cmdSetMode, modeRTTY));
|
||||
usbCommands.append(COMMAND(num++, "RTTYR", commandButton, cmdSetMode, modeRTTY_R));
|
||||
usbCommands.append(COMMAND(num++, "PSK", commandButton, cmdSetMode, modePSK));
|
||||
usbCommands.append(COMMAND(num++, "PSKR", commandButton, cmdSetMode, modePSK_R));
|
||||
usbCommands.append(COMMAND(num++, "DV", commandButton, cmdSetMode, modeDV));
|
||||
usbCommands.append(COMMAND(num++, "DD", commandButton, cmdSetMode, modeDD));
|
||||
usbCommands.append(COMMAND(num++, "Mode LSB", commandButton, cmdSetMode, modeLSB));
|
||||
usbCommands.append(COMMAND(num++, "Mode USB", commandButton, cmdSetMode, modeUSB));
|
||||
usbCommands.append(COMMAND(num++, "Mode LSBD", commandButton, cmdSetMode, modeLSB_D));
|
||||
usbCommands.append(COMMAND(num++, "Mode USBD", commandButton, cmdSetMode, modeUSB_D));
|
||||
usbCommands.append(COMMAND(num++, "Mode CW", commandButton, cmdSetMode, modeCW));
|
||||
usbCommands.append(COMMAND(num++, "Mode CWR", commandButton, cmdSetMode, modeCW_R));
|
||||
usbCommands.append(COMMAND(num++, "Mode FM", commandButton, cmdSetMode, modeFM));
|
||||
usbCommands.append(COMMAND(num++, "Mode AM", commandButton, cmdSetMode, modeAM));
|
||||
usbCommands.append(COMMAND(num++, "Mode RTTY", commandButton, cmdSetMode, modeRTTY));
|
||||
usbCommands.append(COMMAND(num++, "Mode RTTYR", commandButton, cmdSetMode, modeRTTY_R));
|
||||
usbCommands.append(COMMAND(num++, "Mode PSK", commandButton, cmdSetMode, modePSK));
|
||||
usbCommands.append(COMMAND(num++, "Mode PSKR", commandButton, cmdSetMode, modePSK_R));
|
||||
usbCommands.append(COMMAND(num++, "Mode DV", commandButton, cmdSetMode, modeDV));
|
||||
usbCommands.append(COMMAND(num++, "Mode DD", commandButton, cmdSetMode, modeDD));
|
||||
usbCommands.append(COMMAND(num++, "Band+", commandButton, cmdSetBandUp, 0x0));
|
||||
usbCommands.append(COMMAND(num++, "Band-", commandButton, cmdSetBandDown, 0x0));
|
||||
usbCommands.append(COMMAND(num++, "23cm", commandButton, cmdGetBandStackReg, band23cm));
|
||||
usbCommands.append(COMMAND(num++, "70cm", commandButton, cmdGetBandStackReg, band70cm));
|
||||
usbCommands.append(COMMAND(num++, "2m", commandButton, cmdGetBandStackReg, band2m));
|
||||
usbCommands.append(COMMAND(num++, "AIR", commandButton, cmdGetBandStackReg, bandAir));
|
||||
usbCommands.append(COMMAND(num++, "WFM", commandButton, cmdGetBandStackReg, bandWFM));
|
||||
usbCommands.append(COMMAND(num++, "4m", commandButton, cmdGetBandStackReg, band4m));
|
||||
usbCommands.append(COMMAND(num++, "6m", commandButton, cmdGetBandStackReg, band6m));
|
||||
usbCommands.append(COMMAND(num++, "10m", commandButton, cmdGetBandStackReg, band10m));
|
||||
usbCommands.append(COMMAND(num++, "12m", commandButton, cmdGetBandStackReg, band12m));
|
||||
usbCommands.append(COMMAND(num++, "15m", commandButton, cmdGetBandStackReg, band15m));
|
||||
usbCommands.append(COMMAND(num++, "17m", commandButton, cmdGetBandStackReg, band17m));
|
||||
usbCommands.append(COMMAND(num++, "20m", commandButton, cmdGetBandStackReg, band20m));
|
||||
usbCommands.append(COMMAND(num++, "30m", commandButton, cmdGetBandStackReg, band30m));
|
||||
usbCommands.append(COMMAND(num++, "40m", commandButton, cmdGetBandStackReg, band40m));
|
||||
usbCommands.append(COMMAND(num++, "60m", commandButton, cmdGetBandStackReg, band60m));
|
||||
usbCommands.append(COMMAND(num++, "80m", commandButton, cmdGetBandStackReg, band80m));
|
||||
usbCommands.append(COMMAND(num++, "160m", commandButton, cmdGetBandStackReg, band160m));
|
||||
usbCommands.append(COMMAND(num++, "630m", commandButton, cmdGetBandStackReg, band630m));
|
||||
usbCommands.append(COMMAND(num++, "2200m", commandButton, cmdGetBandStackReg, band2200m));
|
||||
usbCommands.append(COMMAND(num++, "GEN", commandButton, cmdGetBandStackReg, bandGen));
|
||||
usbCommands.append(COMMAND(num++, "Band 23cm", commandButton, cmdGetBandStackReg, band23cm));
|
||||
usbCommands.append(COMMAND(num++, "Band 70cm", commandButton, cmdGetBandStackReg, band70cm));
|
||||
usbCommands.append(COMMAND(num++, "Band 2m", commandButton, cmdGetBandStackReg, band2m));
|
||||
usbCommands.append(COMMAND(num++, "Band AIR", commandButton, cmdGetBandStackReg, bandAir));
|
||||
usbCommands.append(COMMAND(num++, "Band WFM", commandButton, cmdGetBandStackReg, bandWFM));
|
||||
usbCommands.append(COMMAND(num++, "Band 4m", commandButton, cmdGetBandStackReg, band4m));
|
||||
usbCommands.append(COMMAND(num++, "Band 6m", commandButton, cmdGetBandStackReg, band6m));
|
||||
usbCommands.append(COMMAND(num++, "Band 10m", commandButton, cmdGetBandStackReg, band10m));
|
||||
usbCommands.append(COMMAND(num++, "Band 12m", commandButton, cmdGetBandStackReg, band12m));
|
||||
usbCommands.append(COMMAND(num++, "Band 15m", commandButton, cmdGetBandStackReg, band15m));
|
||||
usbCommands.append(COMMAND(num++, "Band 17m", commandButton, cmdGetBandStackReg, band17m));
|
||||
usbCommands.append(COMMAND(num++, "Band 20m", commandButton, cmdGetBandStackReg, band20m));
|
||||
usbCommands.append(COMMAND(num++, "Band 30m", commandButton, cmdGetBandStackReg, band30m));
|
||||
usbCommands.append(COMMAND(num++, "Band 40m", commandButton, cmdGetBandStackReg, band40m));
|
||||
usbCommands.append(COMMAND(num++, "Band 60m", commandButton, cmdGetBandStackReg, band60m));
|
||||
usbCommands.append(COMMAND(num++, "Band 80m", commandButton, cmdGetBandStackReg, band80m));
|
||||
usbCommands.append(COMMAND(num++, "Band 160m", commandButton, cmdGetBandStackReg, band160m));
|
||||
usbCommands.append(COMMAND(num++, "Band 630m", commandButton, cmdGetBandStackReg, band630m));
|
||||
usbCommands.append(COMMAND(num++, "Band 2200m", commandButton, cmdGetBandStackReg, band2200m));
|
||||
usbCommands.append(COMMAND(num++, "Band GEN", commandButton, cmdGetBandStackReg, bandGen));
|
||||
usbCommands.append(COMMAND(num++, "NR On", commandButton, cmdNone, 0x0));
|
||||
usbCommands.append(COMMAND(num++, "NR Off", commandButton, cmdNone, 0x0));
|
||||
usbCommands.append(COMMAND(num++, "NB On", commandButton, cmdNone, 0x0));
|
||||
|
@ -9435,4 +9477,14 @@ void wfmain::resetUsbCommands()
|
|||
|
||||
void wfmain::receiveUsbSensitivity(int val) {
|
||||
prefs.usbSensitivity = val;
|
||||
}
|
||||
|
||||
void wfmain::receiveUsbSettings(quint8 bright, quint8 orient, quint8 speed, quint8 timeout, QColor color)
|
||||
{
|
||||
qInfo(logUsbControl()) << QString("USB brightness: %0 orentation: %1 speed: %2 timeout: %3 color: %4").arg(bright).arg(orient).arg(speed).arg(timeout).arg(color.name());
|
||||
prefs.usbBrightness = bright;
|
||||
prefs.usbOrientation = orient;
|
||||
prefs.usbSpeed = speed;
|
||||
prefs.usbTimeout = timeout;
|
||||
prefs.usbColor = color;
|
||||
}
|
4
wfmain.h
4
wfmain.h
|
@ -227,6 +227,7 @@ signals:
|
|||
void sendUsbControllerCommands(QVector<COMMAND>* cmds);
|
||||
void sendUsbControllerButtons(QVector<BUTTON>* buts);
|
||||
void sendUsbControllerKnobs(QVector<KNOB>* kbs);
|
||||
void initUsbDefaults(quint8 bright, quint8 orient, quint8 speed, quint8 timeout, QColor color);
|
||||
void setClusterUdpPort(int port);
|
||||
void setClusterEnableUdp(bool udp);
|
||||
void setClusterEnableTcp(bool tcp);
|
||||
|
@ -352,6 +353,8 @@ private slots:
|
|||
void receiveBaudRate(quint32 baudrate);
|
||||
void radioSelection(QList<radio_cap_packet> radios);
|
||||
void receiveUsbSensitivity(int val);
|
||||
void receiveUsbSettings(quint8 bright, quint8 orient, quint8 speed, quint8 timeout, QColor color);
|
||||
|
||||
|
||||
// Added for RC28/Shuttle support
|
||||
void pttToggle(bool);
|
||||
|
@ -1071,7 +1074,6 @@ private:
|
|||
void resetUsbButtons();
|
||||
void resetUsbKnobs();
|
||||
void resetUsbCommands();
|
||||
|
||||
int oldFreqDialVal;
|
||||
|
||||
rigCapabilities rigCaps;
|
||||
|
|
Ładowanie…
Reference in New Issue