xwhatsit gui: added column tester
authorPurdea Andrei <andrei@purdea.ro>
Mon, 8 Jun 2020 20:11:40 +0000 (23:11 +0300)
committerPurdea Andrei <andrei@purdea.ro>
Tue, 9 Jun 2020 00:10:06 +0000 (03:10 +0300)
keyboards/xwhatsit/util/util/columntester.cpp [new file with mode: 0644]
keyboards/xwhatsit/util/util/columntester.h [new file with mode: 0644]
keyboards/xwhatsit/util/util/columntester.ui [new file with mode: 0644]
keyboards/xwhatsit/util/util/device.cpp
keyboards/xwhatsit/util/util/device.h
keyboards/xwhatsit/util/util/hidthread.cpp
keyboards/xwhatsit/util/util/hidthread.h
keyboards/xwhatsit/util/util/mainwindow.cpp
keyboards/xwhatsit/util/util/mainwindow.h
keyboards/xwhatsit/util/util/mainwindow.ui
keyboards/xwhatsit/util/util/util.pro

diff --git a/keyboards/xwhatsit/util/util/columntester.cpp b/keyboards/xwhatsit/util/util/columntester.cpp
new file mode 100644 (file)
index 0000000..fd6ff9e
--- /dev/null
@@ -0,0 +1,60 @@
+#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);
+}
diff --git a/keyboards/xwhatsit/util/util/columntester.h b/keyboards/xwhatsit/util/util/columntester.h
new file mode 100644 (file)
index 0000000..9919fb3
--- /dev/null
@@ -0,0 +1,33 @@
+#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
diff --git a/keyboards/xwhatsit/util/util/columntester.ui b/keyboards/xwhatsit/util/util/columntester.ui
new file mode 100644 (file)
index 0000000..9323836
--- /dev/null
@@ -0,0 +1,70 @@
+<?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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;*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.&lt;/p&gt;&lt;p&gt;*NOTE2: These are physical columns, so if the keyboard skips some columns those are still listed here&lt;/p&gt;&lt;p&gt;*NOTE3: Normal keyboard input is disabled while this window is open.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
index d3f71bb..c126044 100644 (file)
@@ -187,6 +187,41 @@ void Device::eraseEeprom()
     }
 }
 
+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);
index 28dfbbd..b589076 100644 (file)
@@ -22,6 +22,7 @@ public:
     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();
index 8de6ea5..2ba02e7 100644 (file)
@@ -9,6 +9,7 @@ HidThread::HidThread(Communication &comm, QObject *parent) : QThread(parent), co
     enter_bootloader_path = "";
     autoenter_mode = false;
     close_monitored_device = false;
+    shift_data_path = "";
 }
 
 HidThread::~HidThread()
@@ -72,6 +73,14 @@ void HidThread::closeMonitoredDevice()
     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;
@@ -80,7 +89,8 @@ void HidThread::run()
     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;
@@ -90,6 +100,8 @@ void HidThread::run()
             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) &&
@@ -99,7 +111,8 @@ void HidThread::run()
                             (!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);
             }
@@ -119,6 +132,19 @@ void HidThread::run()
             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 {
index aa208da..8bef089 100644 (file)
@@ -23,6 +23,7 @@ public:
     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);
@@ -42,6 +43,8 @@ private:
     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;
index 356dd73..697e8c3 100644 (file)
@@ -7,6 +7,7 @@
 #include <QMetaType>
 #include <QMenu>
 #include <QAction>
+#include "columntester.h"
 
 Q_DECLARE_METATYPE(std::vector<std::string>)
 Q_DECLARE_METATYPE(std::string)
@@ -102,6 +103,7 @@ void MainWindow::on_listWidget_itemSelectionChanged()
     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);
 }
 
@@ -155,3 +157,16 @@ void MainWindow::on_signalLevelPushButton_clicked()
     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);
+}
index e2a318c..5f47cd9 100644 (file)
@@ -35,6 +35,8 @@ private slots:
     void on_signalLevelPushButton_clicked();
     void ShowContextMenu(const QPoint &pos);
 
+    void on_columnTesterButton_clicked();
+
 private:
     Ui::MainWindow *ui;
     Communication &comm;
index 93ab12d..d3b0837 100644 (file)
       </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>
index 6b7d92a..a02ee30 100644 (file)
@@ -32,7 +32,8 @@ SOURCES += \
     device.cpp \
     hidthread.cpp \
     kbd_defs.cpp \
-    signal_level.cpp
+    signal_level.cpp \
+    columntester.cpp
 
 HEADERS += \
         mainwindow.h \
@@ -41,7 +42,8 @@ HEADERS += \
     device.h \
     hidthread.h \
     kbd_defs.h \
-    signal_level.h
+    signal_level.h \
+    columntester.h
 
 unix:!macx {
     LIBS += -lhidapi-libusb
@@ -63,7 +65,8 @@ win32 {
 FORMS += \
         mainwindow.ui \
     monitorwindow.ui \
-    signal_level.ui
+    signal_level.ui \
+    columntester.ui
 
 # Default rules for deployment.
 qnx: target.path = /tmp/$${TARGET}/bin