From 499584f856f320787bb0d72f095bde3ab0c69742 Mon Sep 17 00:00:00 2001 From: unai_71 Date: Tue, 10 Mar 2026 18:33:34 +0000 Subject: [PATCH 1/4] plan: define MainWindow Qt widget interface --- include/MainWindow.hpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 include/MainWindow.hpp diff --git a/include/MainWindow.hpp b/include/MainWindow.hpp new file mode 100644 index 0000000..77cde2f --- /dev/null +++ b/include/MainWindow.hpp @@ -0,0 +1,32 @@ +#pragma once +// MainWindow.hpp +// SPDX-License-Identifier: GPL-3.0-only +// Author: Unai Blazquez + +#include +#include +#include +#include + +/// @brief Minimal GUI window that displays the last integer received +/// from the ConsumerThread. Never blocks — values arrive via +/// Qt's queued signal/slot mechanism. +class MainWindow : public QWidget +{ + Q_OBJECT + + public: + explicit MainWindow(QWidget* parent = nullptr); + + /// @brief Returns the current text shown in the value label (for testing). + QString lastDisplayedText() const; + + public slots: + /// @brief Slot connected to ConsumerThread::valueReceived. + /// Updates the label with the new value. + void onValueReceived(int value); + + private: + QLabel* m_title_label; + QLabel* m_value_label; +}; From 040cf974f4f430ce6a8c07ce3a2776a219b752bb Mon Sep 17 00:00:00 2001 From: unai_71 Date: Tue, 10 Mar 2026 18:34:56 +0000 Subject: [PATCH 2/4] feat: add failing tests for MainWindow display behaviour --- tests/test_main_window.cxx | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/test_main_window.cxx diff --git a/tests/test_main_window.cxx b/tests/test_main_window.cxx new file mode 100644 index 0000000..02173b9 --- /dev/null +++ b/tests/test_main_window.cxx @@ -0,0 +1,47 @@ +#include + +#include +#include +#include +#include + +#include "MainWindow.hpp" + +// QWidget-based tests need a full QApplication (not QCoreApplication). +// Use offscreen platform so tests run headless in containers. +static int argc_ = 0; +static char* argv_[] = {nullptr}; +static struct SetupOffscreen +{ + SetupOffscreen() { qputenv("QT_QPA_PLATFORM", "offscreen"); } +} setup_offscreen_; +static QApplication app_(argc_, argv_); + +TEST(MainWindowTest, LabelUpdatesOnValueReceived) +{ + MainWindow window; + // Simulate receiving a value from ConsumerThread + window.onValueReceived(42); + + // The label should display the received value + EXPECT_NE(window.lastDisplayedText().toStdString().find("42"), + std::string::npos); +} + +TEST(MainWindowTest, LabelUpdatesMultipleTimes) +{ + MainWindow window; + window.onValueReceived(10); + window.onValueReceived(20); + window.onValueReceived(30); + + // Label should show the most recent value + EXPECT_NE(window.lastDisplayedText().toStdString().find("30"), + std::string::npos); +} + +TEST(MainWindowTest, WindowTitleIsSet) +{ + MainWindow window; + EXPECT_FALSE(window.windowTitle().isEmpty()); +} From 32426b3028d0d99234512483d720e2e343b5872c Mon Sep 17 00:00:00 2001 From: unai_71 Date: Tue, 10 Mar 2026 18:36:08 +0000 Subject: [PATCH 3/4] feat: implement MainWindow label update on valueReceived --- src/app/MainWindow.cxx | 33 +++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 20 ++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/app/MainWindow.cxx diff --git a/src/app/MainWindow.cxx b/src/app/MainWindow.cxx new file mode 100644 index 0000000..a640d4f --- /dev/null +++ b/src/app/MainWindow.cxx @@ -0,0 +1,33 @@ +// MainWindow.cxx +// SPDX-License-Identifier: GPL-3.0-or-later +// Author: Unai Blazquez + +#include "MainWindow.hpp" + +MainWindow::MainWindow(QWidget* parent) : QWidget(parent) +{ + setWindowTitle("Azkoyen IPC Monitor"); + setMinimumSize(320, 120); + + auto* layout = new QVBoxLayout(this); + + m_title_label = new QLabel("Last received value:", this); + m_value_label = new QLabel("(waiting...)", this); + + // Make the value label stand out a bit + QFont font = m_value_label->font(); + font.setPointSize(24); + font.setBold(true); + m_value_label->setFont(font); + m_value_label->setAlignment(Qt::AlignCenter); + + layout->addWidget(m_title_label); + layout->addWidget(m_value_label); +} + +QString MainWindow::lastDisplayedText() const { return m_value_label->text(); } + +void MainWindow::onValueReceived(int value) +{ + m_value_label->setText(QString::number(value)); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0d90812..30538ed 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,3 +53,23 @@ target_link_libraries(test_consumer ) add_test(NAME test_consumer COMMAND test_consumer) + +add_executable(test_main_window + test_main_window.cxx + ${CMAKE_SOURCE_DIR}/src/app/MainWindow.cxx + ${CMAKE_SOURCE_DIR}/include/MainWindow.hpp +) + +target_include_directories(test_main_window PRIVATE ${CMAKE_SOURCE_DIR}/include) + +target_link_libraries(test_main_window + PRIVATE + core + gtest + gtest_main + Qt5::Core + Qt5::Widgets + Qt5::Test +) + +add_test(NAME test_main_window COMMAND test_main_window) From 9cc912b0a30e8b03b08ca9e315d804e6fa72ac31 Mon Sep 17 00:00:00 2001 From: unai_71 Date: Tue, 10 Mar 2026 18:50:32 +0000 Subject: [PATCH 4/4] feat: add app entry point and CMake target for Qt GUI app --- CMakeLists.txt | 9 ++++++++ src/app/main.cxx | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/app/main.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c77dc1..bc87918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,15 @@ add_library(core target_include_directories(core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(core PUBLIC Qt5::Core) +# Main Application +add_executable(app + src/app/main.cxx + src/app/MainWindow.cxx + include/MainWindow.hpp +) +target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(app PRIVATE core Qt5::Widgets) + #tests enable_testing() add_subdirectory(tests) diff --git a/src/app/main.cxx b/src/app/main.cxx new file mode 100644 index 0000000..b533a86 --- /dev/null +++ b/src/app/main.cxx @@ -0,0 +1,58 @@ +// main.cxx +// SPDX-License-Identifier: GPL-3.0-or-later +// Author: Unai Blazquez + +#include +#include +#include +#include + +#include "Consumer.hpp" +#include "MainWindow.hpp" +#include "Producer.hpp" +#include "UnixIpcBridge.hpp" + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + + const std::string socket_path = "/tmp/azkoyen.sock"; + const std::string sysfs_path = "./fake_sysfs_input"; + + // 1. Consumer — listens on the socket, emits Qt signal on receive + ConsumerThread consumer(socket_path); + + // 2. GUI — minimal window that displays received values + MainWindow window; + window.show(); + + // Connect consumer signal → window slot (auto-queued across threads, + // so the GUI never blocks even if the producer is stuck in cool-down) + QObject::connect(&consumer, &ConsumerThread::valueReceived, &window, + &MainWindow::onValueReceived); + + consumer.start(); + + // 3. Bridge — sends ints over the UNIX domain socket + UnixIpcBridge bridge(socket_path); + + // 4. Producer — reads sysfs, generates random int, sends via bridge. + // Logs to a file instead of console (console is for the consumer). + std::ofstream log_file("producer.log", std::ios::app); + + Producer producer( + sysfs_path, [&bridge](int value) { bridge.send(value); }, + []() { return std::rand() % 1000; }, + [&log_file](const std::string& msg) { log_file << msg << std::endl; }); + + producer.start(); + + // 5. Run the Qt event loop (GUI stays responsive, signals are delivered) + int result = app.exec(); + + // 6. Graceful shutdown + producer.stop(); + consumer.stop(); + + return result; +}