diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index 4b5474f2..154d0f6c 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -34,7 +34,7 @@ Supercell Wx uses code from the following dependencies: | [MapLibre Native](https://maplibre.org/projects/maplibre-native/) | [BSD 2-Clause "Simplified" License](https://spdx.org/licenses/BSD-2-Clause.html) | | [nunicode](https://bitbucket.org/alekseyt/nunicode/src/master/) | [MIT License](https://spdx.org/licenses/MIT.html) | Modified for MapLibre Native | | [OpenSSL](https://www.openssl.org/) | [OpenSSL License](https://spdx.org/licenses/OpenSSL.html) | -| [Qt](https://www.qt.io/) | [GNU Lesser General Public License v3.0 only](https://spdx.org/licenses/LGPL-3.0-only.html) | Qt Core, Qt GUI, Qt Multimedia, Qt Network, Qt OpenGL, Qt Positioning, Qt SQL, Qt SVG, Qt Widgets
Additional Licenses: https://doc.qt.io/qt-6/licenses-used-in-qt.html | +| [Qt](https://www.qt.io/) | [GNU Lesser General Public License v3.0 only](https://spdx.org/licenses/LGPL-3.0-only.html) | Qt Core, Qt GUI, Qt Multimedia, Qt Network, Qt OpenGL, Qt Positioning, Qt Serial Port, Qt SQL, Qt SVG, Qt Widgets
Additional Licenses: https://doc.qt.io/qt-6/licenses-used-in-qt.html | | [re2](https://github.com/google/re2) | [BSD 3-Clause "New" or "Revised" License](https://spdx.org/licenses/BSD-3-Clause.html) | | [spdlog](https://github.com/gabime/spdlog) | [MIT License](https://spdx.org/licenses/MIT.html) | | [SQLite](https://www.sqlite.org/) | Public Domain | diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index cda6c7f2..451c2c81 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -28,6 +28,7 @@ find_package(QT NAMES Qt6 OpenGL OpenGLWidgets Positioning + SerialPort Svg Widgets REQUIRED) @@ -39,6 +40,7 @@ find_package(Qt${QT_VERSION_MAJOR} OpenGL OpenGLWidgets Positioning + SerialPort Svg Widgets REQUIRED) @@ -248,6 +250,7 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp source/scwx/qt/ui/placefile_settings_widget.hpp source/scwx/qt/ui/progress_dialog.hpp source/scwx/qt/ui/radar_site_dialog.hpp + source/scwx/qt/ui/serial_port_dialog.hpp source/scwx/qt/ui/settings_dialog.hpp source/scwx/qt/ui/update_dialog.hpp) set(SRC_UI source/scwx/qt/ui/about_dialog.cpp @@ -272,6 +275,7 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp source/scwx/qt/ui/progress_dialog.cpp source/scwx/qt/ui/radar_site_dialog.cpp source/scwx/qt/ui/settings_dialog.cpp + source/scwx/qt/ui/serial_port_dialog.cpp source/scwx/qt/ui/update_dialog.cpp) set(UI_UI source/scwx/qt/ui/about_dialog.ui source/scwx/qt/ui/alert_dialog.ui @@ -287,6 +291,7 @@ set(UI_UI source/scwx/qt/ui/about_dialog.ui source/scwx/qt/ui/progress_dialog.ui source/scwx/qt/ui/radar_site_dialog.ui source/scwx/qt/ui/settings_dialog.ui + source/scwx/qt/ui/serial_port_dialog.ui source/scwx/qt/ui/update_dialog.ui) set(HDR_UI_SETTINGS source/scwx/qt/ui/settings/hotkey_settings_widget.hpp source/scwx/qt/ui/settings/settings_page_widget.hpp @@ -597,6 +602,7 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::OpenGLWidgets Qt${QT_VERSION_MAJOR}::Multimedia Qt${QT_VERSION_MAJOR}::Positioning + Qt${QT_VERSION_MAJOR}::SerialPort Qt${QT_VERSION_MAJOR}::Svg Boost::json Boost::timer diff --git a/scwx-qt/source/scwx/qt/ui/serial_port_dialog.cpp b/scwx-qt/source/scwx/qt/ui/serial_port_dialog.cpp new file mode 100644 index 00000000..6901e4b6 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/serial_port_dialog.cpp @@ -0,0 +1,199 @@ +#define __STDC_WANT_LIB_EXT1__ 1 + +#include "serial_port_dialog.hpp" +#include "ui_serial_port_dialog.h" + +#include + +#include +#include +#include +#include + +#if defined(_WIN32) +# include +# include +# include +#endif + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +static const std::string logPrefix_ = "scwx::qt::ui::serial_port_dialog"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); + +class SerialPortDialog::Impl +{ +public: + explicit Impl(SerialPortDialog* self) : + self_ {self}, model_ {new QStandardItemModel(self)} + { + } + ~Impl() = default; + + void LogSerialPortInfo(const QSerialPortInfo& info); + void ReadComPortSettings(); + void RefreshSerialDevices(); + + SerialPortDialog* self_; + QStandardItemModel* model_; + + std::string selectedSerialPort_ {"?"}; +}; + +SerialPortDialog::SerialPortDialog(QWidget* parent) : + QDialog(parent), + p {std::make_unique(this)}, + ui(new Ui::SerialPortDialog) +{ + ui->setupUi(this); + + connect(ui->refreshButton, + &QAbstractButton::clicked, + this, + [this]() { p->RefreshSerialDevices(); }); +} + +SerialPortDialog::~SerialPortDialog() +{ + delete ui; +} + +std::string SerialPortDialog::serial_port() +{ + return p->selectedSerialPort_; +} + +void SerialPortDialog::Impl::LogSerialPortInfo(const QSerialPortInfo& info) +{ + logger_->debug("Serial Port: {}", info.portName().toStdString()); + logger_->debug(" Description: {}", info.description().toStdString()); + logger_->debug(" System Loc: {}", info.systemLocation().toStdString()); + logger_->debug(" Manufacturer: {}", info.manufacturer().toStdString()); + logger_->debug(" Vendor ID: {}", info.vendorIdentifier()); + logger_->debug(" Product ID: {}", info.productIdentifier()); + logger_->debug(" Serial No: {}", info.serialNumber().toStdString()); +} + +void SerialPortDialog::Impl::RefreshSerialDevices() +{ + QList availablePorts = QSerialPortInfo::availablePorts(); + + for (auto& port : availablePorts) + { + LogSerialPortInfo(port); + } + + ReadComPortSettings(); +} + +void SerialPortDialog::Impl::ReadComPortSettings() +{ +#if defined(_WIN32) + const LPCTSTR lpSubKey = + TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Ports"); + DWORD ulOptions = 0; + REGSAM samDesired = KEY_READ; + HKEY hkResult; + LSTATUS status; + + // Open Port Settings Key + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, lpSubKey, ulOptions, samDesired, &hkResult); + + if (status == ERROR_SUCCESS) + { + DWORD dwIndex = 0; + TCHAR valueName[MAX_PATH]; + LPDWORD lpReserved = nullptr; + DWORD type; + TCHAR valueData[64]; + char buffer[MAX_PATH]; // Buffer for string conversion + + // Number of characters, not including terminating null + static constexpr DWORD maxValueNameSize = + sizeof(valueName) / sizeof(TCHAR) - 1; + DWORD valueNameSize = maxValueNameSize; + + // Number of bytes + DWORD valueDataSize = sizeof(valueData); + + static constexpr std::size_t bufferSize = sizeof(buffer); + + // Enumerate each port value + while ((status = RegEnumValue(hkResult, + dwIndex, + valueName, + &valueNameSize, + lpReserved, + &type, + (LPBYTE) &valueData, + &valueDataSize)) == ERROR_SUCCESS || + status == ERROR_MORE_DATA) + { + // Validate port value + if (status == ERROR_SUCCESS && // + type == REG_SZ && // + valueNameSize >= 5 && // COM#: + valueNameSize < sizeof(buffer) - 1 && // Strip off : + valueDataSize > sizeof(TCHAR) && // Null character + _tcsncmp(valueName, TEXT("COM"), 3) == 0) + { + errno_t error; + std::size_t returnValue; + + // Get port name + if ((error = wcstombs_s(&returnValue, + buffer, + sizeof(buffer), + valueName, + valueNameSize - 1)) != 0) + { + logger_->error( + "Error converting registry value name to string: {}", + returnValue); + continue; + } + + std::string portName = buffer; + + // Get port data + if ((error = wcstombs_s(&returnValue, + buffer, + sizeof(buffer), + valueData, + sizeof(buffer) - 1)) != 0) + { + logger_->error( + "Error converting registry value data to string: {}", + returnValue); + continue; + } + + std::string portData = buffer; + + logger_->debug("Port {} has data \"{}\"", portName, portData); + } + + valueNameSize = maxValueNameSize; + valueDataSize = sizeof(valueData); + ++dwIndex; + } + + RegCloseKey(hkResult); + } + else + { + logger_->warn("Could not open COM port settings registry key: {}", + status); + } +#endif +} + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/serial_port_dialog.hpp b/scwx-qt/source/scwx/qt/ui/serial_port_dialog.hpp new file mode 100644 index 00000000..8bd3ee2c --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/serial_port_dialog.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace Ui +{ +class SerialPortDialog; +} + +namespace scwx +{ +namespace qt +{ +namespace ui +{ +class SerialPortDialog : public QDialog +{ + Q_OBJECT + Q_DISABLE_COPY_MOVE(SerialPortDialog) + +public: + explicit SerialPortDialog(QWidget* parent = nullptr); + ~SerialPortDialog(); + + std::string serial_port(); + +private: + class Impl; + std::unique_ptr p; + Ui::SerialPortDialog* ui; +}; + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/serial_port_dialog.ui b/scwx-qt/source/scwx/qt/ui/serial_port_dialog.ui new file mode 100644 index 00000000..87065d6a --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/serial_port_dialog.ui @@ -0,0 +1,92 @@ + + + SerialPortDialog + + + + 0 + 0 + 400 + 300 + + + + Select Serial Port + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + &Refresh + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + + + + + + + + buttonBox + accepted() + SerialPortDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SerialPortDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +