mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
367 lines
12 KiB
367 lines
12 KiB
/* |
|
This file is part of the KDE Control Center Module for Joysticks |
|
|
|
SPDX-FileCopyrightText: 2003, 2012 Martin Koller <[email protected]> |
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "joywidget.h" |
|
#include "caldialog.h" |
|
#include "joydevice.h" |
|
#include "poswidget.h" |
|
|
|
#include <QApplication> |
|
#include <QCheckBox> |
|
#include <QFontMetrics> |
|
#include <QHBoxLayout> |
|
#include <QHeaderView> |
|
#include <QLabel> |
|
#include <QPushButton> |
|
#include <QStyle> |
|
#include <QTableWidget> |
|
#include <QTimer> |
|
#include <QVBoxLayout> |
|
|
|
#include <KComboBox> |
|
#include <KLocalizedString> |
|
#include <KMessageBox> |
|
#include <KMessageWidget> |
|
#include <KUrlCompletion> |
|
|
|
#include <stdio.h> |
|
|
|
static QString PRESSED = I18N_NOOP("PRESSED"); |
|
|
|
class TableWidget : public QTableWidget |
|
{ |
|
public: |
|
TableWidget(int row, int col) |
|
: QTableWidget(row, col) |
|
{ |
|
} |
|
|
|
QSize sizeHint() const override |
|
{ |
|
return QSize(150, 100); // return a smaller size than the Qt default(256, 192) |
|
} |
|
}; |
|
|
|
JoyWidget::JoyWidget(QWidget *parent) |
|
: QWidget(parent) |
|
, idle(nullptr) |
|
, joydev(nullptr) |
|
{ |
|
QVBoxLayout *mainVbox = new QVBoxLayout(this); |
|
mainVbox->setContentsMargins(0, 0, 0, 0); |
|
|
|
// create area to show an icon + message if no joystick was detected |
|
{ |
|
messageBox = new KMessageWidget(this); |
|
messageBox->setMessageType(KMessageWidget::Error); |
|
messageBox->setCloseButtonVisible(false); |
|
messageBox->hide(); |
|
messageBox->setWordWrap(true); |
|
|
|
mainVbox->addWidget(messageBox); |
|
} |
|
|
|
QHBoxLayout *devHbox = new QHBoxLayout; |
|
devHbox->addWidget(new QLabel(i18n("Device:"))); |
|
devHbox->addWidget(device = new KComboBox(true)); |
|
|
|
device->setInsertPolicy(QComboBox::NoInsert); |
|
KUrlCompletion *kc = new KUrlCompletion(KUrlCompletion::FileCompletion); |
|
device->setCompletionObject(kc); |
|
device->setAutoDeleteCompletionObject(true); |
|
connect(device, SIGNAL(activated(QString)), this, SLOT(deviceChanged(QString))); |
|
connect(device, SIGNAL(returnPressed(QString)), this, SLOT(deviceChanged(QString))); |
|
devHbox->setStretchFactor(device, 3); |
|
|
|
QHBoxLayout *hbox = new QHBoxLayout; |
|
|
|
mainVbox->addLayout(devHbox); |
|
mainVbox->addLayout(hbox); |
|
|
|
QVBoxLayout *vboxLeft = new QVBoxLayout; |
|
vboxLeft->addWidget(new QLabel(i18nc("Cue for deflection of the stick", "Position:"))); |
|
vboxLeft->addWidget(xyPos = new PosWidget); |
|
|
|
vboxLeft->addWidget(trace = new QCheckBox(i18n("Show trace"))); |
|
connect(trace, &QAbstractButton::toggled, this, &JoyWidget::traceChanged); |
|
|
|
QVBoxLayout *vboxMid = new QVBoxLayout; |
|
|
|
QVBoxLayout *vboxRight = new QVBoxLayout; |
|
|
|
// calculate the column width we need |
|
QFontMetrics fm(font()); |
|
int colWidth = qMax(fm.horizontalAdvance(PRESSED), fm.horizontalAdvance(QStringLiteral("-32767"))) + 10; // -32767 largest string |
|
|
|
vboxMid->addWidget(new QLabel(i18n("Buttons:"))); |
|
buttonTbl = new TableWidget(0, 1); |
|
buttonTbl->setSelectionMode(QAbstractItemView::NoSelection); |
|
buttonTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); |
|
buttonTbl->setHorizontalHeaderLabels(QStringList(i18n("State"))); |
|
buttonTbl->setSortingEnabled(false); |
|
buttonTbl->horizontalHeader()->setSectionsClickable(false); |
|
buttonTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); |
|
buttonTbl->horizontalHeader()->resizeSection(0, colWidth); |
|
buttonTbl->verticalHeader()->setSectionsClickable(false); |
|
vboxMid->addWidget(buttonTbl); |
|
|
|
vboxRight->addWidget(new QLabel(i18n("Axes:"))); |
|
axesTbl = new TableWidget(0, 1); |
|
axesTbl->setSelectionMode(QAbstractItemView::NoSelection); |
|
axesTbl->setEditTriggers(QAbstractItemView::NoEditTriggers); |
|
axesTbl->setHorizontalHeaderLabels(QStringList(i18n("Value"))); |
|
axesTbl->setSortingEnabled(false); |
|
axesTbl->horizontalHeader()->setSectionsClickable(false); |
|
axesTbl->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); |
|
axesTbl->horizontalHeader()->resizeSection(0, colWidth); |
|
axesTbl->verticalHeader()->setSectionsClickable(false); |
|
vboxRight->addWidget(axesTbl); |
|
|
|
hbox->addLayout(vboxLeft); |
|
hbox->addLayout(vboxMid); |
|
hbox->addLayout(vboxRight); |
|
|
|
// calibrate button |
|
calibrate = new QPushButton(i18n("Calibrate")); |
|
connect(calibrate, &QAbstractButton::clicked, this, &JoyWidget::calibrateDevice); |
|
calibrate->setEnabled(false); |
|
|
|
vboxLeft->addStretch(); |
|
vboxLeft->addWidget(calibrate); |
|
|
|
// set up a timer for idle processing of joystick events |
|
idle = new QTimer(this); |
|
connect(idle, &QTimer::timeout, this, &JoyWidget::checkDevice); |
|
|
|
// check which devicefiles we have |
|
init(); |
|
} |
|
|
|
JoyWidget::~JoyWidget() |
|
{ |
|
delete joydev; |
|
} |
|
|
|
void JoyWidget::init() |
|
{ |
|
// check which devicefiles we have |
|
int i; |
|
bool first = true; |
|
char dev[30]; |
|
|
|
device->clear(); |
|
buttonTbl->setRowCount(0); |
|
axesTbl->setRowCount(0); |
|
|
|
for (i = 0; i < 5; i++) // check the first 5 devices |
|
{ |
|
sprintf(dev, "/dev/js%d", i); // first look in /dev |
|
JoyDevice *joy = new JoyDevice(dev); |
|
|
|
if (joy->open() != JoyDevice::SUCCESS) { |
|
delete joy; |
|
sprintf(dev, "/dev/input/js%d", i); // then look in /dev/input |
|
joy = new JoyDevice(dev); |
|
|
|
if (joy->open() != JoyDevice::SUCCESS) { |
|
delete joy; |
|
continue; // try next number |
|
} |
|
} |
|
|
|
// we found one |
|
|
|
device->addItem(QStringLiteral("%1 (%2)").arg(joy->text()).arg(joy->device())); |
|
|
|
// display values for first device |
|
if (first) { |
|
showDeviceProps(joy); // this sets the joy object into this->joydev |
|
first = false; |
|
} else |
|
delete joy; |
|
} |
|
|
|
/* KDE 4: Remove this check(and i18n) when all KCM wrappers properly test modules */ |
|
if (device->count() == 0) { |
|
messageBox->show(); |
|
messageBox->setText(QStringLiteral("<qt>%1</qt>") |
|
.arg(i18n("No joystick device automatically found on this computer.<br />" |
|
"Checks were done in /dev/js[0-4] and /dev/input/js[0-4]<br />" |
|
"If you know that there is one attached, please enter the correct device file."))); |
|
} |
|
} |
|
|
|
void JoyWidget::traceChanged(bool state) |
|
{ |
|
xyPos->showTrace(state); |
|
} |
|
|
|
void JoyWidget::restoreCurrDev() |
|
{ |
|
if (!joydev) // no device open |
|
{ |
|
device->setEditText(QString()); |
|
calibrate->setEnabled(false); |
|
} else { |
|
// try to find the current open device in the combobox list |
|
int index = device->findText(joydev->device(), Qt::MatchContains); |
|
|
|
if (index == -1) // the current open device is one the user entered (not in the list) |
|
device->setEditText(joydev->device()); |
|
else |
|
device->setEditText(device->itemText(index)); |
|
} |
|
} |
|
|
|
void JoyWidget::deviceChanged(const QString &dev) |
|
{ |
|
// find "/dev" in given string |
|
int start, stop; |
|
QString devName; |
|
|
|
if ((start = dev.indexOf(QLatin1String("/dev"))) == -1) { |
|
KMessageBox::sorry(this, |
|
i18n("The given device name is invalid (does not contain /dev).\n" |
|
"Please select a device from the list or\n" |
|
"enter a device file, like /dev/js0."), |
|
i18n("Unknown Device")); |
|
|
|
restoreCurrDev(); |
|
return; |
|
} |
|
|
|
if ((stop = dev.indexOf(QLatin1Char(')'), start)) != -1) // seems to be text selected from our list |
|
devName = dev.mid(start, stop - start); |
|
else |
|
devName = dev.mid(start); |
|
|
|
if (joydev && (devName == joydev->device())) |
|
return; // user selected the current device; ignore it |
|
|
|
JoyDevice *joy = new JoyDevice(devName); |
|
JoyDevice::ErrorCode ret = joy->open(); |
|
|
|
if (ret != JoyDevice::SUCCESS) { |
|
KMessageBox::error(this, joy->errText(ret), i18n("Device Error")); |
|
|
|
delete joy; |
|
restoreCurrDev(); |
|
return; |
|
} |
|
|
|
showDeviceProps(joy); |
|
} |
|
|
|
void JoyWidget::showDeviceProps(JoyDevice *joy) |
|
{ |
|
joydev = joy; |
|
|
|
buttonTbl->setRowCount(joydev->numButtons()); |
|
|
|
axesTbl->setRowCount(joydev->numAxes()); |
|
if (joydev->numAxes() >= 2) { |
|
axesTbl->setVerticalHeaderItem(0, new QTableWidgetItem(i18n("1(x)"))); |
|
axesTbl->setVerticalHeaderItem(1, new QTableWidgetItem(i18n("2(y)"))); |
|
} |
|
|
|
calibrate->setEnabled(true); |
|
idle->start(0); |
|
|
|
// make both tables use the same space for header; this looks nicer |
|
// TODO: Don't know how to do this in Qt4; the following does no longer work |
|
// Probably by setting a sizeHint for every single header item ? |
|
/* |
|
buttonTbl->verticalHeader()->setFixedWidth(qMax(buttonTbl->verticalHeader()->width(), |
|
axesTbl->verticalHeader()->width())); |
|
axesTbl->verticalHeader()->setFixedWidth(buttonTbl->verticalHeader()->width()); |
|
*/ |
|
} |
|
|
|
void JoyWidget::checkDevice() |
|
{ |
|
if (!joydev) |
|
return; // no open device yet |
|
|
|
JoyDevice::EventType type; |
|
int number, value; |
|
|
|
if (!joydev->getEvent(type, number, value)) |
|
return; |
|
|
|
if (type == JoyDevice::BUTTON) { |
|
if (!buttonTbl->item(number, 0)) |
|
buttonTbl->setItem(number, 0, new QTableWidgetItem()); |
|
|
|
if (value == 0) // button release |
|
buttonTbl->item(number, 0)->setText(QStringLiteral("-")); |
|
else |
|
buttonTbl->item(number, 0)->setText(PRESSED); |
|
} |
|
|
|
if (type == JoyDevice::AXIS) { |
|
if (number == 0) // x-axis |
|
xyPos->changeX(value); |
|
|
|
if (number == 1) // y-axis |
|
xyPos->changeY(value); |
|
|
|
if (!axesTbl->item(number, 0)) |
|
axesTbl->setItem(number, 0, new QTableWidgetItem()); |
|
|
|
axesTbl->item(number, 0)->setText(QStringLiteral("%1").arg(int(value))); |
|
} |
|
} |
|
|
|
void JoyWidget::calibrateDevice() |
|
{ |
|
if (!joydev) |
|
return; // just to be save |
|
|
|
JoyDevice::ErrorCode ret = joydev->initCalibration(); |
|
|
|
if (ret != JoyDevice::SUCCESS) { |
|
KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); |
|
return; |
|
} |
|
|
|
if (KMessageBox::messageBox(this, |
|
KMessageBox::Information, |
|
i18n("<qt>Calibration is about to check the precision.<br /><br />" |
|
"<b>Please move all axes to their center position and then " |
|
"do not touch the joystick anymore.</b><br /><br />" |
|
"Click OK to start the calibration.</qt>"), |
|
i18n("Calibration"), |
|
KStandardGuiItem::ok(), |
|
KStandardGuiItem::cancel()) |
|
!= KMessageBox::Ok) |
|
return; |
|
|
|
idle->stop(); // stop the joystick event getting; this must be done inside the calibrate dialog |
|
|
|
CalDialog dlg(this, joydev); |
|
dlg.calibrate(); |
|
|
|
// user canceled somewhere during calibration, therefore the device is in a bad state |
|
if (dlg.result() == QDialog::Rejected) |
|
joydev->restoreCorr(); |
|
|
|
idle->start(0); // continue with event getting |
|
} |
|
|
|
void JoyWidget::resetCalibration() |
|
{ |
|
if (!joydev) |
|
return; // just to be save |
|
|
|
JoyDevice::ErrorCode ret = joydev->restoreCorr(); |
|
|
|
if (ret != JoyDevice::SUCCESS) { |
|
KMessageBox::error(this, joydev->errText(ret), i18n("Communication Error")); |
|
} else { |
|
KMessageBox::information(this, i18n("Restored all calibration values for joystick device %1.", joydev->device()), i18n("Calibration Success")); |
|
} |
|
}
|
|
|