Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
if: ${{ matrix.language == 'cpp' }}
run: |
sudo apt-get update
sudo apt-get install --yes git build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev
sudo apt-get install --yes git build-essential qtbase5-dev libqt5serialport5-dev libqt5websockets5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev

- name: Temporarily downgrade CMake to 3.28.3 # Please remove if GitHub has updated Cmake (greater than 3.30.0)
uses: jwlawson/actions-setup-cmake@v2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/qt5_6.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,9 @@ jobs:
- name: 📥 Install Qt
uses: jurplel/install-qt-action@v4
with:
version: ${{ inputs.qt_version == '6' && '6.7' || '5.15.*' }}
version: ${{ inputs.qt_version == '6' && '6.8' || '5.15.*' }}
target: 'desktop'
modules: ${{ inputs.qt_version == '6' && 'qtserialport' || '' }}
modules: ${{ inputs.qt_version == '6' && 'qtserialport qtwebsockets' || '' }}
cache: 'true'
cache-key-prefix: 'cache-qt-windows'

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed: Python 3.12 crashes (#1747)
- osX Grabber: Use ScreenCaptureKit under macOS 15 and above
- Removed maximum LED number constraint from Matrix layout schema which was not synced with the UI behaviour (#1804)
- Fixed bespoke WebSocket implementation by using of QWebSockets (#1816, #1448, #1247, #1130)

**JSON-API**
- Refactored JSON-API to ensure consistent authorization behaviour across sessions and single requests with token authorization.
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ message(STATUS "Found Qt Version: ${QT_VERSION}")
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
set(QT_MIN_VERSION "6.2.2")
else()
set(QT_MIN_VERSION "5.5.0")
set(QT_MIN_VERSION "5.9.0")
endif()

if("${QT_VERSION}" VERSION_LESS "${QT_MIN_VERSION}")
Expand Down
14 changes: 7 additions & 7 deletions doc/development/CompileHowto.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/

### Ubuntu

**amd64 (Jammy):**
**amd64 (Noble Numbat):**
```console
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name jammy
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name noble
```

### Fedora

**amd64 (39):**
**amd64 (41):**
```console
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name 39
wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/docker-compile.sh && chmod +x *.sh && ./docker-compile.sh --name 41
```

## Cross compilation on amd64 for developers
Expand All @@ -61,14 +61,14 @@ cd $HYPERION_HOME

```console
sudo apt-get update
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5websockets5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev libftdi1-dev
```

**Ubuntu (22.04+) - Qt6 based**

```console
sudo apt-get update
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libxkbcommon-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config libftdi1-dev
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libqt6websockets6-dev libxkbcommon-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config libftdi1-dev
```

**For Linux X11/XCB grabber support**
Expand Down Expand Up @@ -110,7 +110,7 @@ See [AUR](https://aur.archlinux.org/packages/?O=0&SeB=nd&K=hyperion&outdated=&SB
The following dependencies are needed to build hyperion.ng on fedora.
```console
sudo dnf -y groupinstall "Development Tools"
sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel libftdi1-dev
sudo dnf install python3-devel qt-devel qt6-qtbase-devel qt6-qtserialport-devel qt6-qtwebsockets-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel libftdi1-dev
```
After installing the dependencies, you can continue with the compile instructions later on this page (the more detailed way..).

Expand Down
8 changes: 6 additions & 2 deletions libsrc/api/JsonAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -922,13 +922,17 @@ void JsonAPI::handleSchemaGetCommand(const QJsonObject& /*message*/, const JsonA
// Add infor about the type of setting elements
QJsonObject settingTypes;
QJsonArray globalSettingTypes;
for (const QString &type : SettingsTable().getGlobalSettingTypes()) {

SettingsTable settingsTable;
for (const QString &type : settingsTable.getGlobalSettingTypes())
{
globalSettingTypes.append(type);
}
settingTypes.insert("globalProperties", globalSettingTypes);

QJsonArray instanceSettingTypes;
for (const QString &type : SettingsTable().getInstanceSettingTypes()) {
for (const QString &type : settingsTable.getInstanceSettingTypes())
{
instanceSettingTypes.append(type);
}
settingTypes.insert("instanceProperties", instanceSettingTypes);
Expand Down
21 changes: 10 additions & 11 deletions libsrc/leddevice/dev_ftdi/ProviderFtdi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ int ProviderFtdi::open()

Debug(_log, "Opening FTDI device=%s", QSTRING_CSTR(_deviceName));

FTDI_CHECK_RESULT((rc = ftdi_usb_open_string(_ftdic, QSTRING_CSTR(_deviceName))) < 0);
FTDI_CHECK_RESULT((rc = ftdi_usb_open_string(_ftdic, QSTRING_CSTR(_deviceName))) < 0)
/* doing this disable resets things if they were in a bad state */
FTDI_CHECK_RESULT((rc = ftdi_disable_bitbang(_ftdic)) < 0);
FTDI_CHECK_RESULT((rc = ftdi_setflowctrl(_ftdic, SIO_DISABLE_FLOW_CTRL)) < 0);
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0x00, BITMODE_RESET)) < 0);
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0xff, BITMODE_MPSSE)) < 0);
FTDI_CHECK_RESULT((rc = ftdi_disable_bitbang(_ftdic)) < 0)
FTDI_CHECK_RESULT((rc = ftdi_setflowctrl(_ftdic, SIO_DISABLE_FLOW_CTRL)) < 0)
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0x00, BITMODE_RESET)) < 0)
FTDI_CHECK_RESULT((rc = ftdi_set_bitmode(_ftdic, 0xff, BITMODE_MPSSE)) < 0)

double reference_clock = 60e6;
int divisor = (reference_clock / 2 / _baudRate_Hz) - 1;
Expand All @@ -86,7 +86,7 @@ int ProviderFtdi::open()
pinDirection
};

FTDI_CHECK_RESULT((rc = ftdi_write_data(_ftdic, buf.data(), buf.size())) != buf.size());
FTDI_CHECK_RESULT(static_cast<size_t>(rc = ftdi_write_data(_ftdic, buf.data(), static_cast<int>(buf.size()))) != buf.size())

_isDeviceReady = true;
return rc;
Expand Down Expand Up @@ -134,7 +134,7 @@ int ProviderFtdi::writeBytes(const qint64 size, const uint8_t *data)
// SET_BITS_LOW takes 2 arguments, so we're inserting data in -3 position from the end
buf.insert(buf.end() - 3, &data[0], &data[size]);

FTDI_CHECK_RESULT((rc = ftdi_write_data(_ftdic, buf.data(), buf.size())) != buf.size());
FTDI_CHECK_RESULT(static_cast<size_t>(rc = ftdi_write_data(_ftdic, buf.data(), static_cast<int>(buf.size()))) != buf.size())
return rc;
}

Expand All @@ -152,7 +152,7 @@ QJsonObject ProviderFtdi::discover(const QJsonObject & /*params*/)
struct ftdi_device_list *curdev = devlist;
QMap<QString, uint8_t> deviceIndexes;

while (curdev)
while (curdev != nullptr)
{
libusb_device_descriptor desc;
int rc = libusb_get_device_descriptor(curdev->dev, &desc);
Expand All @@ -161,8 +161,7 @@ QJsonObject ProviderFtdi::discover(const QJsonObject & /*params*/)
QString vendorIdentifier = QString("0x%1").arg(desc.idVendor, 4, 16, QChar{'0'});
QString productIdentifier = QString("0x%1").arg(desc.idProduct, 4, 16, QChar{'0'});
QString vendorAndProduct = QString("%1:%2")
.arg(vendorIdentifier)
.arg(productIdentifier);
.arg(vendorIdentifier,productIdentifier);
uint8_t deviceIndex = deviceIndexes.value(vendorAndProduct, 0);

char serial_string[128] = {0};
Expand All @@ -174,7 +173,7 @@ QJsonObject ProviderFtdi::discover(const QJsonObject & /*params*/)
QString ftdiOpenString;
if(!serialNumber.isEmpty())
{
ftdiOpenString = QString("s:%1:%2").arg(vendorAndProduct).arg(serialNumber);
ftdiOpenString = QString("s:%1:%2").arg(vendorAndProduct, serialNumber);
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion libsrc/python/PythonProgram.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ PythonProgram::PythonProgram(const QString& name, Logger* log) :
PyEval_RestoreThread(mainThreadState);
_tstate = Py_NewInterpreter();
#else
PyThreadState* prev = PyThreadState_Swap(NULL);
PyThreadState_Swap(NULL);

// Create a new interpreter configuration object
PyInterpreterConfig config{};
Expand Down
11 changes: 5 additions & 6 deletions libsrc/utils/ImageResampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,15 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i

outputImage.resize(outputWidth, outputHeight);

int xDestStart, xDestEnd;
int yDestStart, yDestEnd;
int xDestStart {0};
int xDestEnd = {outputWidth-1};
int yDestStart = {0};
int yDestEnd = {outputWidth-1};

switch (_flipMode)
{
case FlipMode::NO_CHANGE:
xDestStart = 0;
xDestEnd = outputWidth-1;
yDestStart = 0;
yDestEnd = outputHeight-1;
//use the initalized values
break;
case FlipMode::HORIZONTAL:
xDestStart = 0;
Expand Down
9 changes: 5 additions & 4 deletions libsrc/webserver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS WebSockets REQUIRED)

file(GLOB_RECURSE webFiles RELATIVE ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig/*)
file(RELATIVE_PATH webConfigPath ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/assets/webconfig)
Expand Down Expand Up @@ -28,13 +29,13 @@ add_library(webserver
${CMAKE_SOURCE_DIR}/libsrc/webserver/StaticFileServing.cpp
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.h
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebJsonRpc.cpp
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.h
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketClient.cpp
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketUtils.h
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketJsonHandler.h
${CMAKE_SOURCE_DIR}/libsrc/webserver/WebSocketJsonHandler.cpp
${CMAKE_BINARY_DIR}/WebConfig.qrc
)
)

target_link_libraries(webserver
Qt${QT_VERSION_MAJOR}::WebSockets
hyperion
hyperion-utils
hyperion-api
Expand Down
46 changes: 38 additions & 8 deletions libsrc/webserver/QtHttpClientWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "QtHttpReply.h"
#include "QtHttpServer.h"
#include "QtHttpHeader.h"
#include "WebSocketClient.h"
#include "WebSocketJsonHandler.h"
#include "WebJsonRpc.h"

#include <QCryptographicHash>
Expand All @@ -27,8 +27,9 @@ QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, const bool& localCo
, m_localConnection(localConnection)
, m_websocketClient(nullptr)
, m_webJsonRpc (nullptr)
, m_websocketServer (nullptr)
{
connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
connect(m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
}

QString QtHttpClientWrapper::getGuid (void)
Expand All @@ -50,6 +51,11 @@ void QtHttpClientWrapper::onClientDataReceived (void)
{
if (m_sockClient != Q_NULLPTR)
{
if (!m_sockClient->isTransactionStarted())
{
m_sockClient->startTransaction();
}

while (m_sockClient->bytesAvailable () != 0)
{
QByteArray line = m_sockClient->readLine ();
Expand Down Expand Up @@ -162,22 +168,30 @@ void QtHttpClientWrapper::onClientDataReceived (void)
{
case RequestParsed: // a valid request has ben fully parsed
{
// Catch websocket header "Upgrade"
if(m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower() == "websocket")
const auto& upgradeValue = m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower();
if (upgradeValue == "websocket")
{
if(m_websocketClient == Q_NULLPTR)
{
// disconnect this slot from socket for further requests
disconnect(m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
// disabling packet bunching
m_sockClient->setSocketOption(QAbstractSocket::LowDelayOption, 1);
m_sockClient->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
m_websocketClient = new WebSocketClient(m_currentRequest, m_sockClient, m_localConnection, this);
m_sockClient->rollbackTransaction();

QString servername = QCoreApplication::applicationName() + QLatin1Char('/') + HYPERION_VERSION;
QWebSocketServer::SslMode secureMode = m_serverHandle->isSecure() ? QWebSocketServer::SecureMode : QWebSocketServer::NonSecureMode;
m_websocketServer.reset(new QWebSocketServer(servername, secureMode));
connect(m_websocketServer.get(), &QWebSocketServer::newConnection,
this, &QtHttpClientWrapper::onNewWebSocketConnection);

m_websocketServer->handleConnection(m_sockClient);
emit m_sockClient->readyRead();
return;
}

break;
}

m_sockClient->commitTransaction();
// add post data to request and catch /jsonrpc subroute url
if ( m_currentRequest->getCommand() == "POST")
{
Expand Down Expand Up @@ -227,6 +241,8 @@ void QtHttpClientWrapper::onClientDataReceived (void)
case ParsingError: // there was an error durin one of parsing steps
{
m_sockClient->readAll (); // clear remaining buffer to ignore content
m_sockClient->commitTransaction();

QtHttpReply reply (m_serverHandle);
reply.setStatusCode (QtHttpReply::BadRequest);
reply.appendRawData (QByteArrayLiteral ("<h1>Bad Request (HTTP parsing error) !</h1>"));
Expand Down Expand Up @@ -365,3 +381,17 @@ void QtHttpClientWrapper::closeConnection()
}
m_sockClient->close ();
}

void QtHttpClientWrapper::onNewWebSocketConnection() {

// Handle the pending connection
QWebSocket* webSocket = m_websocketServer->nextPendingConnection();
if (webSocket) {
// Manage the WebSocketJsonHandler for this connection
WebSocketJsonHandler* handler = new WebSocketJsonHandler(webSocket);
connect(webSocket, &QWebSocket::disconnected, handler, &QObject::deleteLater);
}
else {
qWarning() << "No pending WebSocket connection!";
}
}
8 changes: 8 additions & 0 deletions libsrc/webserver/QtHttpClientWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include <QObject>
#include <QString>
#include <QWebSocketServer>
#include <QCoreApplication>
#include <QScopedPointer>

class QTcpSocket;

Expand Down Expand Up @@ -39,8 +42,12 @@ class QtHttpClientWrapper : public QObject {
///
void closeConnection();

signals:
void newWebSocketConnection();

private slots:
void onClientDataReceived (void);
void onNewWebSocketConnection();

protected:
ParsingStatus sendReplyToClient (QtHttpReply * reply);
Expand All @@ -59,6 +66,7 @@ protected slots:
WebSocketClient * m_websocketClient;
WebJsonRpc * m_webJsonRpc;
QByteArray m_fragment;
QScopedPointer<QWebSocketServer> m_websocketServer;
};

#endif // QTHTTPCLIENTWRAPPER_H
28 changes: 14 additions & 14 deletions libsrc/webserver/QtHttpServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ QtHttpServer::QtHttpServer (QObject * parent)
, m_netOrigin (NetOrigin::getInstance())
{
m_sockServer = new QtHttpServerWrapper (this);
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected, Qt::UniqueConnection);
}

void QtHttpServer::start (quint16 port)
Expand Down Expand Up @@ -81,27 +81,27 @@ void QtHttpServer::onClientConnected (void)
{
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ())
{
if(m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress()))
if (m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress()))
{
connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
connect(sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);

if (m_useSsl)
{
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock))
if (QSslSocket* ssl = qobject_cast<QSslSocket*> (sock))
{
connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
ssl->setLocalCertificateChain (m_sslCerts);
ssl->setPrivateKey (m_sslKey);
ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
ssl->startServerEncryption ();
connect(ssl, SslErrorSignal(&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
connect(ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
connect(ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
connect(ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
ssl->setLocalCertificateChain(m_sslCerts);
ssl->setPrivateKey(m_sslKey);
ssl->setPeerVerifyMode(QSslSocket::AutoVerifyPeer);
ssl->startServerEncryption();
}
}

QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this);
m_socksClientsHash.insert (sock, wrapper);
QtHttpClientWrapper* wrapper = new QtHttpClientWrapper(sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this);
m_socksClientsHash.insert(sock, wrapper);
emit clientConnected (wrapper->getGuid ());
}
else
Expand Down
Loading
Loading