--- /dev/null
+#include "columntester.h"
+#include "ui_columntester.h"
+#include <QSizePolicy>
+#include <stdio.h>
+#include <QMenu>
+#include <QAction>
+
+ColumnTester::ColumnTester(HidThread &thread, std::string path, QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::ColumnTester),
+ thread(thread),
+ path(path)
+{
+ ui->setupUi(this);
+ int i;
+ for (i=1;i<24;i++)
+ {
+ QPushButton *button = new QPushButton(QString::number(i), this);
+ buttons.push_back(button);
+ button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+ button->setMinimumSize(20, 0);
+ button->setCheckable(true);
+ ui->horizontalLayout->addWidget(button);
+ connect(button, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
+ }
+ this->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
+ this, SLOT(ShowContextMenu(const QPoint &)));
+}
+
+void ColumnTester::ShowContextMenu(const QPoint &pos)
+{
+ QMenu contextMenu(tr("Context menu"), this);
+ QAction action1("Close", this);
+ connect(&action1, SIGNAL(triggered()), this, SLOT(close()));
+ contextMenu.addAction(&action1);
+ contextMenu.exec(mapToGlobal(pos));
+}
+
+ColumnTester::~ColumnTester()
+{
+ for(auto const& value: buttons) {
+ delete value;
+ }
+
+ delete ui;
+}
+
+void ColumnTester::onButtonClicked()
+{
+ uint32_t data = 0;
+ for (int i=0;i<23;i++)
+ {
+ if (buttons[i]->isChecked())
+ {
+ data |= 1 << i;
+ }
+ }
+ thread.shiftData(path, data);
+}
--- /dev/null
+#ifndef COLUMNTESTER_H
+#define COLUMNTESTER_H
+
+#include <QDialog>
+#include <QPoint>
+#include <QPushButton>
+#include <vector>
+#include "hidthread.h"
+
+namespace Ui {
+class ColumnTester;
+}
+
+class ColumnTester : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ColumnTester(HidThread &thread, std::string path, QWidget *parent = nullptr);
+ ~ColumnTester();
+
+private slots:
+ void onButtonClicked();
+ void ShowContextMenu(const QPoint &pos);
+
+private:
+ Ui::ColumnTester *ui;
+ std::vector<QPushButton *> buttons;
+ HidThread &thread;
+ std::string path;
+};
+
+#endif // COLUMNTESTER_H
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ColumnTester</class>
+ <widget class="QDialog" name="ColumnTester">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>807</width>
+ <height>196</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>635</width>
+ <height>135</height>
+ </size>
+ </property>
+ <property name="text">
+ <string><html><head/><body><p>*NOTE1: The column tester is a debugging feature that allows you to set Low/High static output on each column pin. Pressed means logic high. This feature is most useful in conjunction with a voltmeter, to check for functional shift registers, and no broken or shorted column traces.</p><p>*NOTE2: These are physical columns, so if the keyboard skips some columns those are still listed here</p><p>*NOTE3: Normal keyboard input is disabled while this window is open.</p></body></html></string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
}
}
+void Device::shiftData(uint32_t shdata)
+{
+ QMutexLocker locker(&mutex);
+ if (xwhatsit_original_firmware)
+ {
+ throw std::runtime_error("This doesn't work with xwhatsit original firmware");
+ }
+ uint8_t data[33];
+ data[0] = 0;
+ memcpy(data + 1, magic, sizeof(magic));
+ data[2+1] = UTIL_COMM_SHIFT_DATA;
+ data[3+1] = shdata & 0xff;
+ data[4+1] = (shdata >> 8) & 0xff;
+ data[5+1] = (shdata >> 16) & 0xff;
+ data[6+1] = (shdata >> 24) & 0xff;
+ if (-1==hid_write(device, data, sizeof(data)))
+ {
+ printf("hid error: %ls\n", hid_error(device));
+ throw std::runtime_error("hid_write failed to shift data");
+ }
+ if ((sizeof(data)-1)!=hid_read_timeout(device, data, sizeof(data)-1, 1000))
+ {
+ printf("hid error: %ls\n", hid_error(device));
+ throw std::runtime_error("hid_read failed while shifting data");
+ }
+ if ((data[0] != magic[0]) || (data[1] != magic[1]))
+ {
+ throw std::runtime_error("hid_read failed while shifting data -- no magic returned");
+ }
+ if (data[2] != UTIL_COMM_RESPONSE_OK)
+ {
+ throw std::runtime_error("hid_read failed while shifting data -- response not okay");
+ }
+}
+
void Device::disableKeyboard()
{
QMutexLocker locker(&mutex);
bool isVersionAtLeast(uint8_t major, uint8_t mid, uint16_t minor);
void assertVersionIsAtLeast(uint8_t major, uint8_t mid, uint16_t minor);
void eraseEeprom();
+ void shiftData(uint32_t shdata);
std::vector<std::vector<uint8_t>> getThresholds();
std::vector<uint8_t> getKeyState();
std::vector<uint8_t> getKeyboardDetails();
enter_bootloader_path = "";
autoenter_mode = false;
close_monitored_device = false;
+ shift_data_path = "";
}
HidThread::~HidThread()
condition.wakeOne();
}
+void HidThread::shiftData(std::string path, uint32_t shdata)
+{
+ QMutexLocker locker(&mutex);
+ this->shift_data = shdata;
+ this->shift_data_path = path;
+ condition.wakeOne();
+}
+
void HidThread::run()
{
Device *monitoredDevice = nullptr;
forever {
mutex.lock();
bool l_keep_scanning, l_abort, nothing_to_do, l_autoenter_mode, l_close_monitored_device;
- std::string l_enter_bootloader_path, l_monitor_path, l_erase_eeprom_path, l_signal_level_path;
+ uint32_t l_shift_data;
+ std::string l_enter_bootloader_path, l_monitor_path, l_erase_eeprom_path, l_signal_level_path, l_shift_data_path;
do {
l_keep_scanning = this->keep_scanning;
l_enter_bootloader_path = this->enter_bootloader_path;
l_close_monitored_device = this->close_monitored_device;
l_abort = this->abort;
l_erase_eeprom_path = this->erase_eeprom_path;
+ l_shift_data_path = shift_data_path;
+ l_shift_data = shift_data;
nothing_to_do = (!l_keep_scanning) &&
(!l_abort) &&
(l_enter_bootloader_path.size()==0) &&
(!l_close_monitored_device) &&
(l_erase_eeprom_path.size() == 0) &&
(l_signal_level_path.size() == 0) &&
- (signalLevelDevice == nullptr);
+ (signalLevelDevice == nullptr) &&
+ (l_shift_data_path.size() == 0);
if (nothing_to_do) {
condition.wait(&mutex);
}
this->enter_bootloader_path = "";
mutex.unlock();
}
+ if (l_shift_data_path.size() != 0)
+ {
+ try {
+ QScopedPointer<Device> dev(comm.open(l_shift_data_path));
+ dev.data()->assertVersionIsAtLeast(2, 0, 3);
+ dev.data()->shiftData(l_shift_data);
+ } catch (const std::runtime_error &e1) {
+ emit reportError(e1.what());
+ }
+ mutex.lock();
+ this->shift_data_path = "";
+ mutex.unlock();
+ }
if (l_erase_eeprom_path.size() != 0)
{
try {
void signalLevel(std::string path);
void eraseEeprom(std::string path);
void closeMonitoredDevice();
+ void shiftData(std::string path, uint32_t shdata);
Device *connectToDevice(std::string path);
signals:
void scannedDevices(std::vector<std::string> devices);
bool keep_scanning;
bool autoenter_mode;
bool close_monitored_device;
+ uint32_t shift_data;
+ std::string shift_data_path;
std::string enter_bootloader_path;
std::string monitor_path;
std::string signal_level_path;
#include <QMetaType>
#include <QMenu>
#include <QAction>
+#include "columntester.h"
Q_DECLARE_METATYPE(std::vector<std::string>)
Q_DECLARE_METATYPE(std::string)
ui->keypressMinotorPushButton->setEnabled(enabled && !is_xwhatsit);
ui->eraseEepromPushButton->setEnabled(enabled && !is_xwhatsit);
ui->signalLevelPushButton->setEnabled(enabled && !is_xwhatsit);
+ ui->columnTesterButton->setEnabled(enabled && !is_xwhatsit);
Q_UNUSED(is_xwhatsit);
}
ui->listWidget->setEnabled(true);
thread.setScanning(previousScanning);
}
+
+void MainWindow::on_columnTesterButton_clicked()
+{
+ ColumnTester *ctw = new ColumnTester(thread, ui->listWidget->currentItem()->text().toStdString(), this);
+ ctw->setAttribute(Qt::WA_DeleteOnClose);
+ bool previousScanning = thread.setScanning(false);
+ ui->listWidget->setEnabled(false);
+ thread.shiftData(ui->listWidget->currentItem()->text().toStdString(), 0);
+ ctw->exec();
+ ui->listWidget->setEnabled(true);
+ thread.shiftData(ui->listWidget->currentItem()->text().toStdString(), 0);
+ thread.setScanning(previousScanning);
+}
void on_signalLevelPushButton_clicked();
void ShowContextMenu(const QPoint &pos);
+ void on_columnTesterButton_clicked();
+
private:
Ui::MainWindow *ui;
Communication &comm;
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QPushButton" name="enterBootloaderPushbutton">
+ <item row="0" column="1">
+ <widget class="QPushButton" name="eraseEepromPushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
- <string>Enter Bootloader</string>
+ <string>Erase EEPROM</string>
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QPushButton" name="eraseEepromPushButton">
+ <item row="1" column="1">
+ <widget class="QPushButton" name="signalLevelPushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
- <string>Erase EEPROM</string>
+ <string>Signal Level Monitor</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QPushButton" name="pushButton_3">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Collect Test Data</string>
</property>
</widget>
</item>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QPushButton" name="signalLevelPushButton">
+ <item row="0" column="0">
+ <widget class="QPushButton" name="enterBootloaderPushbutton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
- <string>Signal Level Monitor</string>
+ <string>Enter Bootloader</string>
</property>
</widget>
</item>
<item row="2" column="0">
- <widget class="QPushButton" name="pushButton_3">
+ <widget class="QPushButton" name="columnTesterButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
- <string>Collect Test Data</string>
+ <string>Column Tester</string>
</property>
</widget>
</item>
device.cpp \
hidthread.cpp \
kbd_defs.cpp \
- signal_level.cpp
+ signal_level.cpp \
+ columntester.cpp
HEADERS += \
mainwindow.h \
device.h \
hidthread.h \
kbd_defs.h \
- signal_level.h
+ signal_level.h \
+ columntester.h
unix:!macx {
LIBS += -lhidapi-libusb
FORMS += \
mainwindow.ui \
monitorwindow.ui \
- signal_level.ui
+ signal_level.ui \
+ columntester.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin