diff --git a/doorlockd/CMakeLists.txt b/doorlockd/CMakeLists.txt index a1440ea..38480ae 100644 --- a/doorlockd/CMakeLists.txt +++ b/doorlockd/CMakeLists.txt @@ -9,6 +9,11 @@ set(DOORLOCK_VERSION_PATCH 0) set(DOORLOCK_VERSION "${DOORLOCK_VERSION_MAJOR}.${DOORLOCK_VERSION_MINOR}-${DOORLOCK_VERSION_PATCH}") +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +include_directories(${CMAKE_SOURCE_DIR}/src) + MESSAGE(STATUS "doorlockd version: ${DOORLOCK_VERSION}") # Get the current working branch @@ -51,6 +56,8 @@ include_directories(${JSON_INCLUDE_DIR}) find_package (Threads) +find_package(Qt5Widgets) + set(DOORLOCKD_SRCS src/clientmessage.cpp src/clientmessage.h @@ -80,6 +87,8 @@ set(DOORLOCK_CLIENT_SRCS src/response.h src/util.cpp src/util.h + src/qrwidget.cpp + src/qrwidget.h src/doorlock-client.cpp ) @@ -88,7 +97,8 @@ add_executable(doorlockd ${DOORLOCKD_SRCS}) target_link_libraries(doorlockd jsoncpp ldap ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) add_executable(doorlock-client ${DOORLOCK_CLIENT_SRCS}) -target_link_libraries(doorlock-client jsoncpp ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(doorlock-client jsoncpp ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + qrencode Qt5::Widgets) install(TARGETS doorlockd DESTINATION sbin/) install(TARGETS doorlock-client DESTINATION bin/) diff --git a/doorlockd/src/doorlock-client.cpp b/doorlockd/src/doorlock-client.cpp index cb296d0..d307cb4 100644 --- a/doorlockd/src/doorlock-client.cpp +++ b/doorlockd/src/doorlock-client.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -10,6 +11,9 @@ #include "logger.h" #include "response.h" +#include +#include "qrwidget.h" + // Info about doorlock-client version const static std::string version = "doorlock-client-" DOORLOCK_VERSION; @@ -29,20 +33,37 @@ const static std::string subscriptionCommand = // The receive buffer length of the TCP socket const int constexpr SOCKET_BUFFERLENGTH = 2048; +static volatile bool run = true; + +static std::unique_ptr qrWidget = nullptr; + static void doorlock_update(const Clientmessage &msg) { - // TODO develop small X application to show the QR code - char buffer[1024]; - snprintf(buffer, sizeof(buffer), - "qrencode -t ansi %s", - msg.token().c_str()); - system(buffer); + if (qrWidget) { + qrWidget->setQRData(msg.token()); + } +} + +static int startGui(int argc, char** argv) +{ + QApplication app(argc, argv); + + app.setOrganizationName("Binary Kitchen"); + app.setApplicationName("doorlock-client"); + + qrWidget = std::unique_ptr(new QRWidget); + qrWidget->showFullScreen(); + + int retval = app.exec(); + run = false; + return retval; } static int doorlock_client(const std::string &hostname, const unsigned short port) { int retval = 0; + std::thread guiThread; try { tcp::resolver resolver(io_service); @@ -75,7 +96,11 @@ static int doorlock_client(const std::string &hostname, if (!response) throw std::runtime_error("Invalid response from server"); - for (;;) { + guiThread = std::thread([] () { + startGui(0, nullptr); + }); + + for (;run;) { length = socket.read_some(boost::asio::buffer(data), error); if (error) throw boost::system::system_error(error); @@ -94,6 +119,9 @@ static int doorlock_client(const std::string &hostname, } out: + if (guiThread.joinable()) { + guiThread.join(); + } return retval; } @@ -137,7 +165,8 @@ int main(int argc, char** argv) } l(LogLevel::notice, "Starting doorlock-client"); - retval = doorlock_client(hostname, port); + + doorlock_client(hostname, port); out: l(LogLevel::notice, "Stopping doorlock-client"); diff --git a/doorlockd/src/qrwidget.cpp b/doorlockd/src/qrwidget.cpp new file mode 100644 index 0000000..126f506 --- /dev/null +++ b/doorlockd/src/qrwidget.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include +#include + +#include + +#include "qrwidget.h" + +QRWidget::QRWidget(QWidget* parent) : + QWidget(parent), + _data(" ") +{ +} + +void QRWidget::setQRData(const std::string &data) +{ + _data = QString::fromStdString(data); + update(); +} + +void QRWidget::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + + std::unique_ptr qr( + QRcode_encodeString(_data.toStdString().c_str(), 1, QR_ECLEVEL_L, QR_MODE_8, 0), + [] (QRcode* ptr) { + if (ptr) + QRcode_free(ptr); + }); + + if (!qr) + throw std::runtime_error("Error generating QR Code"); + + QColor fg("black"); + QColor bg("white"); + + painter.setBrush(bg); + painter.setPen(Qt::NoPen); + painter.drawRect(0,0,width(),height()); + painter.setBrush(fg); + + const int s=qr->width>0?qr->width:1; + const double w=width(); + const double h=height(); + const double aspect=w/h; + const double scale=((aspect>1.0)?h:w)/s; + + for(int y=0;ydata[xx]; + if(b &0x01) { + const double rx1=x*scale, ry1=y*scale; + QRectF r(rx1, ry1, scale, scale); + painter.drawRects(&r,1); + } + } + } +} diff --git a/doorlockd/src/qrwidget.h b/doorlockd/src/qrwidget.h new file mode 100644 index 0000000..32e6128 --- /dev/null +++ b/doorlockd/src/qrwidget.h @@ -0,0 +1,21 @@ +#ifndef QRWIDGET_H +#define QRWIDGET_H + +#include + +class QRWidget : public QWidget +{ + Q_OBJECT + +public: + explicit QRWidget(QWidget *parent = nullptr); + void setQRData(const std::string &data); + +private: + QString _data; + +protected: + void paintEvent(QPaintEvent *); +}; + +#endif