diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index a6b6c1e19..8fc293570 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -82,6 +82,8 @@ jobs: -v "${{ github.workspace }}:/source:rw" \ -v "/tmp/deps:/tmp/deps:rw" \ -w "/source" \ + -e LANG="C.UTF-8" \ + -e LC_ALL="C.UTF-8" \ ghcr.io/hyperion-project/debian:${{ env.DOCKER_TAG }} \ /bin/bash -c "cmake --preset linux-${{ env.BUILD_TYPE }} ${{ steps.dependencies.outputs.cmakeArgs }} -DPLATFORM=${{ matrix.os.platform }} ${{ env.CPACK_SYSTEM_PROCESSOR }} && cmake --build --preset linux-${{ env.BUILD_TYPE }} --target package && diff --git a/.version b/.version index 3e3c2f1e5..e543fedfc 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.1.1 +2.1.2-beta.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2da80f3b5..78e76f2f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 🔧 Changed +- **Fixes:** + - UI - Language is not selectable (#1877) + - CEC-Handler is not stopped properly + - Qt-Grabber (Windows) does not apply pixel ratio (#1882) - _Thanks to @SolberLight_ + - LED-devices are not retrying to establish connectivity, if supported by the device + - LED-devices are resolving IP-addresses for API and UDP two times in sequence + --- ### 🗑️ Removed +- Removed the ability to revoke API requests which are not issued from the same network segment. This includes the removal of IP-address white- & blacklisting (#1880) +Rationale: Such kind of functionality is better to be dealt with via proper network management and setup, e.g. via firewalls, VLANs. + ## [2.1.1](https://github.com/hyperion-project/hyperion.ng/compare/2.1.1...HEAD) - 2025-06-14 ### 🔧 Changed diff --git a/assets/webconfig/css/hyperion.css b/assets/webconfig/css/hyperion.css index 4bfb03b66..a522b6e2a 100644 --- a/assets/webconfig/css/hyperion.css +++ b/assets/webconfig/css/hyperion.css @@ -245,20 +245,17 @@ table.first_cell_borderless td:first-child{width: 25px !important;} .mdi-lead-pencil, .mdi-delete-forever {font-size: 22px;} /*led preview & led visualisation*/ -body { - --background-var: none; - --width-var: 0px; - --height-var: 0px; -} - #leds_canvas { position:absolute; margin:15px; background-repeat:no-repeat; background-position: center; + --background-var: none; + --width-var: 0px; + --height-var: 0px; } -#leds_canvas:before { +#leds_canvas::before { content: ""; position:absolute; @@ -279,6 +276,10 @@ body { filter: blur(10px); background-size: cover; + + /* Safari optimization for pseudo-elements */ + transform: translateZ(0); + -webkit-transform: translateZ(0); } .led { display:inline-block; border: 1px solid black; position:absolute; opacity:0.8; text-align:center; vertical-align:middle; padding:4px; border-radius:2px;} diff --git a/assets/webconfig/js/content_index.js b/assets/webconfig/js/content_index.js index 66640cce7..c5ba59e1c 100644 --- a/assets/webconfig/js/content_index.js +++ b/assets/webconfig/js/content_index.js @@ -442,7 +442,28 @@ $(window.hyperion).on("cmd-config-setconfig", function (event) { function bindUiHandlers() { - // Side menu link click activation + // Handle language selection + + // Prevent dropdown from closing when selecting language + $('#btn_setlang').on('click', function (e) { + e.stopPropagation(); + }); + + $('#language-select').on('changed.bs.select', function () { + const newLang = $(this).val(); + if (newLang !== storedLang) { + setStorage("langcode", newLang); + location.reload(); + } + }); + + //Close selector, if open (and no change in language happend) + $(document).on("click", function () { + $(".bootstrap-select.open").removeClass("open"); + }); + //End language selection + + // Side smenu link click activation $('#side-menu li a, #side-menu li ul li a').on("click", function () { $('#side-menu').find('.active').removeClass('active'); $(this).addClass('active'); @@ -476,13 +497,6 @@ function bindUiHandlers() { logo.style.display = window.scrollY > 65 ? "none" : ""; }, { passive: true }); - // Language selector - $(".langSelect").off().on("click", function () { - const newLang = $(this).attr("id").replace("lang_", ""); - setStorage("lang", newLang); - location.reload(); - }); - // Toggle top menu for mobile $(".navbar-toggle").off().on("click", function () { const target = $(this).data("target"); diff --git a/assets/webconfig/js/content_network.js b/assets/webconfig/js/content_network.js index 69f49494f..134ef2929 100644 --- a/assets/webconfig/js/content_network.js +++ b/assets/webconfig/js/content_network.js @@ -266,16 +266,11 @@ $(document).ready(function () { $("#conf_cont_tok").insertAfter("#conf_cont_network"); // Initial state check based on server config - checkApiTokenState(window.serverConfig.network.internetAccessAPI || window.serverConfig.network.localApiAuth || storedAccess === 'expert'); - - // Listen for changes on the Internet access API Auth toggle - $('#root_network_internetAccessAPI').on("change", function () { - checkApiTokenState($(this).is(":checked") || $('#root_network_localApiAuth').is(":checked")); - }); + checkApiTokenState(window.serverConfig.network.localApiAuth || storedAccess === 'expert'); // Listen for changes on the local API Auth toggle $('#root_network_localApiAuth').on("change", function () { - checkApiTokenState($(this).is(":checked") || $('#root_network_internetAccessAPI').is(":checked")); + checkApiTokenState($(this).is(":checked")); }); $('#btn_create_tok').off().on('click', function () { @@ -317,7 +312,7 @@ $(document).ready(function () { } function checkApiTokenState(state) { - if (!state) { + if (!state && storedAccess !== 'expert') { $("#conf_cont_tok").hide(); } else { $("#conf_cont_tok").show(); diff --git a/assets/webconfig/js/ledsim.js b/assets/webconfig/js/ledsim.js index 75c8dc62e..711ae9fb9 100644 --- a/assets/webconfig/js/ledsim.js +++ b/assets/webconfig/js/ledsim.js @@ -6,9 +6,9 @@ $(document).ready(function () { var leds; var grabberConfig; var lC = false; - var imageCanvasNodeCtx = document.getElementById("image_preview_canv").getContext("2d"); - var ledsCanvasNodeCtx = document.getElementById("leds_preview_canv").getContext("2d"); - var sigDetectAreaCanvasNodeCtx = document.getElementById("grab_preview_canv").getContext("2d"); + var imageCanvasNodeCtx = document.getElementById("image_preview_canv").getContext("2d", { willReadFrequently: false}); + var ledsCanvasNodeCtx = document.getElementById("leds_preview_canv").getContext("2d", { willReadFrequently: false}); + var sigDetectAreaCanvasNodeCtx = document.getElementById("grab_preview_canv").getContext("2d", { willReadFrequently: false}); var canvas_height; var canvas_width; var twoDPaths = []; @@ -205,12 +205,12 @@ $(document).ready(function () { canvas_width = $('#ledsim_dialog').outerWidth() - 30; $("[id$=_preview_canv]").prop({ "width": canvas_width, "height": canvas_height }); - $("body").get(0).style.setProperty("--width-var", canvas_width + "px"); - $("body").get(0).style.setProperty("--height-var", canvas_height + "px"); + $('#leds_canvas').css("--width-var", canvas_width + "px"); + $('#leds_canvas').css("--height-var", canvas_height + "px"); create2dPaths(); printLedsToCanvas(); - $("body").get(0).style.setProperty("--background-var", "none"); + $('#leds_canvas').css("--background-var", "none"); resetImage(); } @@ -227,7 +227,7 @@ $(document).ready(function () { if (window.ledStreamActive) { requestLedColorsStop(); ledsCanvasNodeCtx.clear(); - $("body").get(0).style.setProperty("--background-var", "none"); + $('#leds_canvas').css("--background-var", "none"); } else { requestLedColorsStart(); } @@ -263,11 +263,13 @@ $(document).ready(function () { $(window.hyperion).on("cmd-ledcolors-ledstream-update", function (event) { if (!modalOpened) { requestLedColorsStop(); - $("body").get(0).style.setProperty("--background-var", "none"); + $('#leds_canvas').css("--background-var", "none"); } else { printLedsToCanvas(event.response.data.leds) - $("body").get(0).style.setProperty("--background-var", "url(" + ($('#leds_preview_canv')[0]).toDataURL("image/jpg") + ") no-repeat top left"); + $('#leds_canvas').css("--background-var", "url(" + ($('#leds_preview_canv')[0]).toDataURL("image/jpg") + ") no-repeat top left"); + // Safari workaround (redraw canvas) + $('#leds_canvas').parent().hide().show(0); } }); diff --git a/assets/webconfig/js/ui_utils.js b/assets/webconfig/js/ui_utils.js index 41dba356a..423917c0c 100644 --- a/assets/webconfig/js/ui_utils.js +++ b/assets/webconfig/js/ui_utils.js @@ -188,40 +188,38 @@ function updateHyperionInstanceListing() { } function initLanguageSelection() { - // Initialize language selection list with supported languages + const $select = $('#language-select'); + $select.empty(); // clear existing options + for (let i = 0; i < availLang.length; i++) { - $("#language-select").append( - '' - ); + $select.append(''); } let langLocale = storedLang; - let langText = ''; + if (!langLocale) { + langLocale = navigator.language?.substring(0, 2) || 'en'; + } - // Test if the language is supported by Hyperion let langIdx = availLang.indexOf(langLocale); - if (langIdx > -1) { - langText = availLangText[langIdx]; - } else { - // If the language is not supported, try the fallback language + if (langIdx === -1) { + // Try fallback langLocale = $.i18n().options.fallbackLocale.substring(0, 2); langIdx = availLang.indexOf(langLocale); - if (langIdx > -1) { - langText = availLangText[langIdx]; - } else { - // Default to English if fallback language is also unsupported - langLocale = 'en'; - langIdx = availLang.indexOf(langLocale); - if (langIdx > -1) { - langText = availLangText[langIdx]; - } - } + } + + if (langIdx === -1) { + // Default to English + langLocale = 'en'; } // Update the language select dropdown - $('#language-select').prop('title', langText); - $("#language-select").val(langIdx); - $("#language-select").selectpicker("refresh"); + $select.val(langLocale); + $select.selectpicker({ + container: 'body', + width: 'fit', + style: 'btn-transparent' + }); + $select.selectpicker('refresh'); } function updateUiOnInstance(inst) { diff --git a/bin/scripts/docker-compile.sh b/bin/scripts/docker-compile.sh index 464adcc22..c9188d2bf 100755 --- a/bin/scripts/docker-compile.sh +++ b/bin/scripts/docker-compile.sh @@ -272,7 +272,9 @@ $DOCKER run --rm --platform=${PLATFORM_ARCHITECTURE} \ ${ENTRYPOINT_OPTION} \ -v "${DEPLOY_PATH}:/deploy" \ -v "${CODE_PATH}/:/source:rw" \ - ${REGISTRY_URL}/${DISTRIBUTION}:${CODENAME} \ + -e LANG="C.UTF-8" \ + -e LC_ALL="C.UTF-8" \ + "${REGISTRY_URL}/${DISTRIBUTION}:${CODENAME}" \ /bin/bash -c "mkdir -p /source/${BUILD_DIR} && cd /source/${BUILD_DIR} && cmake -G Ninja -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${PLATFORM} ${BUILD_ARGS} .. || exit 2 && cmake --build . ${PACKAGES} -- -j $(nproc) || exit 3 || : && diff --git a/cmake/windows/inno/windows.iss.in b/cmake/windows/inno/windows.iss.in index e6e7f4165..1267243c2 100644 --- a/cmake/windows/inno/windows.iss.in +++ b/cmake/windows/inno/windows.iss.in @@ -251,6 +251,7 @@ begin PageDescriptionLabel.Visible := False; PageNameLabel.Visible := False; Bevel1.Visible := False; + RunList.Left := InnerNotebook.Left; with WizardSmallBitmapImage do begin @@ -261,6 +262,32 @@ begin Center := True; Align := alClient; end; + + with FinishedLabel do + begin + Left := InnerNotebook.Left; + Width := InnerNotebook.Width; + end; + + with FinishedHeadingLabel do + begin + Left := InnerNotebook.Left; + Width := InnerNotebook.Width; + Top := 0; + end; + + with WizardBitmapImage2 do + begin + Parent := FinishedHeadingLabel; + Bitmap.AlphaFormat := afDefined; + Width := Parent.Width; + Height := Parent.Height; + AutoSize := True; + Stretch := False; + Center := True; + Align := alClient; + Bitmap := WizardSmallBitmapImage.Bitmap; + end; end; end; diff --git a/include/api/API.h b/include/api/API.h index 1f778bb93..0ea331c69 100644 --- a/include/api/API.h +++ b/include/api/API.h @@ -383,7 +383,7 @@ class API : public QObject // current instance index quint8 _currInstanceIndex; - QSharedPointer _hyperion; + QWeakPointer _hyperionWeak; signals: /// diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 766aab36e..b388e93b0 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -422,5 +422,4 @@ private slots: QSharedPointer _jsonCB; bool _isServiceAvailable; - }; diff --git a/include/api/JsonApiSubscription.h b/include/api/JsonApiSubscription.h index 3d011bbdf..7642d6e01 100644 --- a/include/api/JsonApiSubscription.h +++ b/include/api/JsonApiSubscription.h @@ -13,9 +13,7 @@ class Subscription { Unknown, AdjustmentUpdate, ComponentsUpdate, -#if defined(ENABLE_EFFECTENGINE) EffectsUpdate, -#endif EventUpdate, ImageToLedMappingUpdate, ImageUpdate, @@ -33,9 +31,7 @@ class Subscription { switch (type) { case AdjustmentUpdate: return "adjustment-update"; case ComponentsUpdate: return "components-update"; -#if defined(ENABLE_EFFECTENGINE) case EffectsUpdate: return "effects-update"; -#endif case EventUpdate: return "event-update"; case ImageToLedMappingUpdate: return "imageToLedMapping-update"; case ImageUpdate: return "ledcolors-imagestream-update"; @@ -55,9 +51,7 @@ class Subscription { switch (type) { case AdjustmentUpdate: case ComponentsUpdate: -#if defined(ENABLE_EFFECTENGINE) case EffectsUpdate: -#endif case ImageToLedMappingUpdate: case ImageUpdate: case LedColorsUpdate: @@ -109,9 +103,7 @@ class ApiSubscriptionRegister { static const SubscriptionLookupMap subscriptionLookup { { {"adjustment-update"}, { Subscription::AdjustmentUpdate, true} }, { {"components-update"}, { Subscription::ComponentsUpdate, true} }, -#if defined(ENABLE_EFFECTENGINE) { {"effects-update"}, { Subscription::EffectsUpdate, true} }, -#endif { {"event-update"}, { Subscription::EventUpdate, true} }, { {"imageToLedMapping-update"}, { Subscription::ImageToLedMappingUpdate, true} }, { {"ledcolors-imagestream-update"}, { Subscription::ImageUpdate, false} }, diff --git a/include/api/JsonCallbacks.h b/include/api/JsonCallbacks.h index a090b780e..84d10a0db 100644 --- a/include/api/JsonCallbacks.h +++ b/include/api/JsonCallbacks.h @@ -196,16 +196,16 @@ private slots: Logger *_log; quint8 _instanceID; - QSharedPointer _hyperion; + QWeakPointer _hyperionWeak; /// The peer address of the client QString _peerAddress; /// pointer of comp register - QSharedPointer _componentRegister; + QWeakPointer _componentRegisterWeak; /// priority muxer instance - QSharedPointer _prioMuxer; + QWeakPointer _prioMuxerWeak; /// contains active subscriptions QSet _subscribedCommands; diff --git a/include/api/JsonInfo.h b/include/api/JsonInfo.h index 58f0eab9a..2a3cbca30 100644 --- a/include/api/JsonInfo.h +++ b/include/api/JsonInfo.h @@ -7,29 +7,31 @@ #include #include +#include + class JsonInfo { public: - static QJsonObject getInfo(const Hyperion* hyperion, Logger* log); - static QJsonArray getAdjustmentInfo(const Hyperion* hyperion, Logger* log); - static QJsonArray getPrioritiestInfo(const Hyperion* hyperion); + static QJsonObject getInfo(const QSharedPointer& hyperionInstance, Logger* log); + static QJsonArray getAdjustmentInfo(const QSharedPointer& hyperionInstance, Logger* log); + static QJsonArray getPrioritiestInfo(const QSharedPointer& hyperionInstance); static QJsonArray getPrioritiestInfo(int currentPriority, const PriorityMuxer::InputsMap& activeInputs); static QJsonArray getEffects(); static QJsonArray getEffectSchemas(); static QJsonArray getAvailableScreenGrabbers(); static QJsonArray getAvailableVideoGrabbers(); static QJsonArray getAvailableAudioGrabbers(); - static QJsonObject getGrabbers(const Hyperion* hyperion); + static QJsonObject getGrabbers(const QSharedPointer& hyperionInstance); static QJsonObject getAvailableLedDevices(); static QJsonObject getCecInfo(); static QJsonArray getServices(); - static QJsonArray getComponents(const Hyperion* hyperion); + static QJsonArray getComponents(const QSharedPointer& hyperionInstance); static QJsonArray getInstanceInfo(); - static QJsonArray getActiveEffects(const Hyperion* hyperion); - static QJsonArray getActiveColors(const Hyperion* hyperion); - static QJsonArray getTransformationInfo(const Hyperion* hyperion); + static QJsonArray getActiveEffects(const QSharedPointer& hyperionInstance); + static QJsonArray getActiveColors(const QSharedPointer& hyperionInstance); + static QJsonArray getTransformationInfo(const QSharedPointer& hyperionInstance); static QJsonObject getSystemInfo(); QJsonObject discoverSources (const QString& sourceType, const QJsonObject& params); diff --git a/include/blackborder/BlackBorderProcessor.h b/include/blackborder/BlackBorderProcessor.h index ce372bf17..2e1cb4ecf 100644 --- a/include/blackborder/BlackBorderProcessor.h +++ b/include/blackborder/BlackBorderProcessor.h @@ -26,7 +26,8 @@ namespace hyperion { Q_OBJECT public: - BlackBorderProcessor(Hyperion* hyperion, QObject* parent); + BlackBorderProcessor(const QSharedPointer& hyperionInstance, QObject* parent); + ~BlackBorderProcessor() override; /// /// Return the current (detected) border @@ -114,7 +115,9 @@ namespace hyperion private: /// Hyperion instance - Hyperion* _hyperion; + QWeakPointer _hyperionWeak; + /// Logger instance + Logger* _log; /// /// Updates the current border based on the newly detected border. Returns true if the diff --git a/include/boblightserver/BoblightServer.h b/include/boblightserver/BoblightServer.h index 153177e13..68c6c6442 100644 --- a/include/boblightserver/BoblightServer.h +++ b/include/boblightserver/BoblightServer.h @@ -6,6 +6,7 @@ // Qt includes #include #include +#include // Hyperion includes #include @@ -31,7 +32,7 @@ class BoblightServer : public QObject /// @param hyperion Hyperion instance /// @param port port number on which to start listening for connections /// - BoblightServer(Hyperion* hyperion, const QJsonDocument& config); + explicit BoblightServer(const QSharedPointer& hyperionInstance, const QJsonDocument& config); ~BoblightServer() override; /// @@ -76,8 +77,8 @@ private slots: void closedConnection(BoblightClientConnection * connection); private: - /// Hyperion instance - Hyperion * _hyperion; + /// Hyperion instance pointer + QWeakPointer _hyperionWeak; /// The TCP server object QTcpServer * _server; diff --git a/include/cec/CECHandler.h b/include/cec/CECHandler.h index d3a19c11d..6639f347b 100644 --- a/include/cec/CECHandler.h +++ b/include/cec/CECHandler.h @@ -33,7 +33,6 @@ class CECHandler : public QObject Q_OBJECT public: CECHandler(const QJsonDocument& config, QObject * parent = nullptr); - ~CECHandler() override; QString scan() const; diff --git a/include/db/DBMigrationManager.h b/include/db/DBMigrationManager.h index f5b342070..474dfb111 100644 --- a/include/db/DBMigrationManager.h +++ b/include/db/DBMigrationManager.h @@ -22,6 +22,7 @@ class DBMigrationManager : public DBManager bool upgradeGlobalSettings_2_0_12(semver::version& currentVersion, QJsonObject& config); bool upgradeGlobalSettings_2_0_16(semver::version& currentVersion, QJsonObject& config); bool upgradeGlobalSettings_2_1_0(semver::version& currentVersion, QJsonObject& config); + bool upgradeGlobalSettings_2_1_2(semver::version& currentVersion, QJsonObject& config); bool upgradeInstanceSettings(const semver::version& currentVersion, quint8 instance, QJsonObject& config); bool upgradeInstanceSettings_alpha_9(semver::version& currentVersion, quint8 instance, QJsonObject& config); diff --git a/include/effectengine/Effect.h b/include/effectengine/Effect.h index ad4419305..3d4f8dd6f 100644 --- a/include/effectengine/Effect.h +++ b/include/effectengine/Effect.h @@ -24,7 +24,7 @@ class Effect : public QThread friend class EffectModule; - Effect(Hyperion* hyperion + Effect(const QSharedPointer& hyperionInstance , int priority , int timeout , const QString& script @@ -84,7 +84,8 @@ public slots: bool setModuleParameters(); void addImage(); - Hyperion* _hyperion; + /// Hyperion instance pointer + QWeakPointer _hyperionWeak; const int _priority; diff --git a/include/effectengine/EffectEngine.h b/include/effectengine/EffectEngine.h index 7d807f862..01ec83f89 100644 --- a/include/effectengine/EffectEngine.h +++ b/include/effectengine/EffectEngine.h @@ -27,7 +27,7 @@ class EffectEngine : public QObject Q_OBJECT public: - EffectEngine(Hyperion * hyperion); + explicit EffectEngine(const QSharedPointer& hyperionInstance); ~EffectEngine() override; std::list getEffects() const { return _availableEffects; } @@ -101,7 +101,8 @@ private slots: void waitForEffectsToStop(); private: - Hyperion * _hyperion; + /// Hyperion instance pointer + QWeakPointer _hyperionWeak; std::list _availableEffects; @@ -112,7 +113,7 @@ private slots: Logger * _log; // The global effect file handler - EffectFileHandler* _effectFileHandler; + EffectFileHandler * _effectFileHandler; QEventLoop _eventLoop; int _remainingEffects; diff --git a/include/forwarder/MessageForwarder.h b/include/forwarder/MessageForwarder.h index 8fd1d927a..d4458a403 100644 --- a/include/forwarder/MessageForwarder.h +++ b/include/forwarder/MessageForwarder.h @@ -14,6 +14,7 @@ #include #include #include +#include // Utils includes #include @@ -133,10 +134,11 @@ private slots: SettingsTable _settings; /// Hyperion instance forwarded - QSharedPointer _hyperion; + /// Hyperion instance + QWeakPointer _hyperionWeak; /// Muxer instance - QSharedPointer _muxer; + QWeakPointer _muxerWeak; // JSON connections for forwarding QList _jsonTargets; diff --git a/include/grabber/qt/QtGrabber.h b/include/grabber/qt/QtGrabber.h index abf666bd0..e95084b01 100644 --- a/include/grabber/qt/QtGrabber.h +++ b/include/grabber/qt/QtGrabber.h @@ -98,11 +98,17 @@ class QtGrabber : public Grabber private slots: /// - /// @brief is called whenever the current _screen changes it's geometry + /// @brief is called whenever the current screen changes it's geometry /// @param geo The new geometry /// void geometryChanged(const QRect &geo); + /// + /// @brief is called whenever the current pixel ratio changed + /// @param dpi The new dots per inch + /// + void pixelRatioChanged(qreal dpi); + private: /// diff --git a/include/hyperion/BGEffectHandler.h b/include/hyperion/BGEffectHandler.h index a92b1dbce..de755930c 100644 --- a/include/hyperion/BGEffectHandler.h +++ b/include/hyperion/BGEffectHandler.h @@ -17,7 +17,8 @@ class BGEffectHandler : public QObject Q_OBJECT public: - BGEffectHandler(Hyperion* hyperion = nullptr); + BGEffectHandler(const QSharedPointer& hyperionInstance); + ~BGEffectHandler() override; /// /// @brief Disconnect from connected signals @@ -48,7 +49,7 @@ private slots: private: /// Hyperion instance pointer - Hyperion* _hyperion; + QWeakPointer _hyperionWeak; Logger * _log; QJsonDocument _bgEffectConfig; diff --git a/include/hyperion/CaptureCont.h b/include/hyperion/CaptureCont.h index 689e92c79..4b86e5df7 100644 --- a/include/hyperion/CaptureCont.h +++ b/include/hyperion/CaptureCont.h @@ -16,12 +16,23 @@ class CaptureCont : public QObject { Q_OBJECT public: - CaptureCont(Hyperion* hyperion); + explicit CaptureCont(const QSharedPointer& hyperionInstance); + ~CaptureCont() override; void setScreenCaptureEnable(bool enable); void setVideoCaptureEnable(bool enable); void setAudioCaptureEnable(bool enable); + /// + /// @brief Start Capture Control and its timers + /// + void start(); + + /// + /// @brief Stop Capture Control and its timers + /// + void stop(); + private slots: /// /// @brief Handle component state change of Video- (V4L/MF) and Screen capture @@ -72,24 +83,24 @@ private slots: private: - /// Hyperion instance - Hyperion* _hyperion; + /// Hyperion instance pointer + QWeakPointer _hyperionWeak; /// Reflect state of screen capture and prio bool _screenCaptureEnabled; int _screenCapturePriority; QString _screenCaptureName; - QTimer* _screenCaptureInactiveTimer; + QScopedPointer _screenCaptureInactiveTimer; /// Reflect state of video capture and prio bool _videoCaptureEnabled; int _videoCapturePriority; QString _videoCaptureName; - QTimer* _videoInactiveTimer; + QScopedPointer _videoInactiveTimer; /// Reflect state of audio capture and prio bool _audioCaptureEnabled; int _audioCapturePriority; QString _audioCaptureName; - QTimer* _audioCaptureInactiveTimer; + QScopedPointer _audioCaptureInactiveTimer; }; diff --git a/include/hyperion/ComponentRegister.h b/include/hyperion/ComponentRegister.h index aa2023404..ef3ab825b 100644 --- a/include/hyperion/ComponentRegister.h +++ b/include/hyperion/ComponentRegister.h @@ -8,6 +8,8 @@ #include #include +#include +#include class Hyperion; @@ -22,7 +24,7 @@ class ComponentRegister : public QObject Q_OBJECT public: - ComponentRegister(Hyperion* hyperion); + explicit ComponentRegister(const QSharedPointer& hyperionInstance); ~ComponentRegister() override; /// @@ -30,9 +32,10 @@ class ComponentRegister : public QObject /// @param comp The component from enum /// @return True if component is running else false. Not found is -1 /// - int isComponentEnabled(hyperion::Components comp) const; - - /// contains all components and their state + int extracted(hyperion::Components &comp) const; + int isComponentEnabled(hyperion::Components comp) const; + + /// contains all components and their state std::map getRegister() const { return _componentStates; } signals: @@ -68,7 +71,7 @@ private slots: private: /// Hyperion instance - Hyperion * _hyperion; + QWeakPointer _hyperionWeak; /// Logger instance Logger * _log; /// current state of all components diff --git a/include/hyperion/Grabber.h b/include/hyperion/Grabber.h index 5ad7c45b2..aba5899b5 100644 --- a/include/hyperion/Grabber.h +++ b/include/hyperion/Grabber.h @@ -175,6 +175,8 @@ protected slots: // Device states + bool _isCropping; + /// Is the device available? bool _isAvailable; diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index aef0598a1..83ca3cd85 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -47,6 +47,8 @@ // settings utils #include +#include + // Forward class declaration class ImageProcessor; class LinearColorSmoothing; @@ -56,7 +58,7 @@ class Logger; /// The main class of Hyperion. This gives other 'users' access to the attached LedDevice through /// the priority muxer. /// -class Hyperion : public QObject +class Hyperion : public QObject, public QEnableSharedFromThis { Q_OBJECT public: @@ -70,7 +72,9 @@ class Hyperion : public QObject /// explicit Hyperion(quint8 instance, QObject* parent = nullptr); - ImageProcessor* getImageProcessor() const { return _imageProcessor.get(); } + ~Hyperion() override; + + QSharedPointer getImageProcessor() const { return _imageProcessor; } /// /// @brief Get instance index of this instance @@ -508,8 +512,10 @@ private slots: /// void handleSourceAvailability(int priority); -private: +signals: + void isSetNewComponentState(hyperion::Components component, bool state); +private: void updateLedColorAdjustment(int ledCount, const QJsonObject& colors); void updateLedLayout(const QJsonArray& ledLayout); @@ -519,34 +525,45 @@ private slots: /// Settings manager of this instance QScopedPointer _settingsManager; - /// Register that holds component states - QSharedPointer _componentRegister; - /// The specifiation of the led frame construction and picture integration LedString _ledString; + std::vector _ledStringColorOrder; + + /// Register that holds component states + QSharedPointer _componentRegister; + /// Image Processor QSharedPointer _imageProcessor; - std::vector _ledStringColorOrder; + /// The adjustment from raw colors to led colors + QScopedPointer _raw2ledAdjustment; /// The priority muxer QSharedPointer _muxer; - /// The adjustment from raw colors to led colors - QScopedPointer _raw2ledAdjustment; - /// The actual LedDeviceWrapper - QScopedPointer _ledDeviceWrapper; + QSharedPointer _ledDeviceWrapper; /// The smoothing LedDevice - QScopedPointer _deviceSmooth; + QSharedPointer _deviceSmooth; + + /// Capture control for Daemon native capture + QSharedPointer _captureCont; + + /// Background effect instance, kept active to react on setting changes + QSharedPointer _BGEffectHandler; #if defined(ENABLE_EFFECTENGINE) /// Effect engine QSharedPointer _effectEngine; #endif +#if defined(ENABLE_BOBLIGHT_SERVER) + /// Boblight instance + QSharedPointer _boblightServer; +#endif + /// Logger instance Logger * _log; @@ -556,21 +573,11 @@ private slots: QString _colorOrder; QSize _layoutGridSize; - /// Background effect instance, kept active to react on setting changes - QScopedPointer _BGEffectHandler; - /// Capture control for Daemon native capture - QScopedPointer _captureCont; - /// buffer for leds (with adjustment) std::vector _ledBuffer; VideoMode _currVideoMode = VideoMode::VIDEO_2D; -#if defined(ENABLE_BOBLIGHT_SERVER) - /// Boblight instance - QScopedPointer _boblightServer; -#endif - QElapsedTimer _imageTimer; // Timer for controlling image emission frequency QElapsedTimer _rawLedDataTimer; // Timer for controlling rawLedColors emission frequency QElapsedTimer _ledDeviceDataTimer; // Timer for controlling LedDevice data emission frequency diff --git a/include/hyperion/ImageProcessor.h b/include/hyperion/ImageProcessor.h index f588b221d..f8e2cc8dd 100644 --- a/include/hyperion/ImageProcessor.h +++ b/include/hyperion/ImageProcessor.h @@ -35,8 +35,7 @@ class ImageProcessor : public QObject /// @param[in] ledString LedString data /// @param[in] hyperion Hyperion instance pointer /// - ImageProcessor(const LedString& ledString, Hyperion* hyperion); - + explicit ImageProcessor(const LedString& ledString, const QSharedPointer& hyperionInstance); ~ImageProcessor() override; /// @@ -270,13 +269,14 @@ private slots: void handleSettingsUpdate(settings::type type, const QJsonDocument& config); private: + /// Logger instance + Logger* _log; - Logger * _log; /// The Led-string specification LedString _ledString; /// The processor for black border detection - hyperion::BlackBorderProcessor * _borderProcessor; + QScopedPointer _borderProcessor; /// The mapping of image-pixels to LEDs QSharedPointer _imageToLedColors; @@ -292,5 +292,5 @@ private slots: int _reducedPixelSetFactorFactor; /// Hyperion instance pointer - Hyperion* _hyperion; + QWeakPointer _hyperionWeak; }; diff --git a/include/hyperion/ImageToLedsMap.h b/include/hyperion/ImageToLedsMap.h index 2b33cf14e..5b12067ab 100644 --- a/include/hyperion/ImageToLedsMap.h +++ b/include/hyperion/ImageToLedsMap.h @@ -53,6 +53,7 @@ namespace hyperion const std::vector & leds, int reducedProcessingFactor = 0, int accuraryLevel = 0); + ~ImageToLedsMap() override; /// /// Returns the width of the indexed image diff --git a/include/hyperion/LinearColorSmoothing.h b/include/hyperion/LinearColorSmoothing.h index 127f0072b..96d8cd758 100644 --- a/include/hyperion/LinearColorSmoothing.h +++ b/include/hyperion/LinearColorSmoothing.h @@ -82,7 +82,7 @@ class LinearColorSmoothing : public QObject /// @param config The smoothing configuration /// @param hyperion The hyperion parent instance /// - LinearColorSmoothing(const QJsonObject &config, Hyperion *hyperion); + explicit LinearColorSmoothing(const QJsonObject &config, const QSharedPointer& hyperionInstance); ~LinearColorSmoothing() override; /// LED values as input for the smoothing filter @@ -188,10 +188,10 @@ private slots: Logger *_log; /// Hyperion instance - Hyperion *_hyperion; + QWeakPointer _hyperionWeak; /// priority muxer instance - QSharedPointer _prioMuxer; + QWeakPointer _prioMuxerWeak; /// The interval at which to update the leds (msec) int _updateInterval; diff --git a/include/hyperion/PriorityMuxer.h b/include/hyperion/PriorityMuxer.h index d26b670aa..caaf6cd7c 100644 --- a/include/hyperion/PriorityMuxer.h +++ b/include/hyperion/PriorityMuxer.h @@ -289,8 +289,8 @@ private slots: bool _sourceAutoSelectEnabled; // Timer to update Muxer times independent - QScopedPointer _updateTimer; + QScopedPointer _updateTimer; - QScopedPointer _timer; - QScopedPointer _blockTimer; + QScopedPointer _timer; + QScopedPointer _blockTimer; }; diff --git a/include/leddevice/LedDevice.h b/include/leddevice/LedDevice.h index bd4ed880f..8d60d4a3c 100644 --- a/include/leddevice/LedDevice.h +++ b/include/leddevice/LedDevice.h @@ -108,6 +108,12 @@ class LedDevice : public QObject /// void setAutoStart(bool isAutoStart); + /// @brief Define, if a device can be recovered by retrying open attempts in an error scenrario + /// + /// @param[in] isRecoverable + /// + void setIsRecoverable(bool isRecoverable = true) { _isDeviceRecoverable = isRecoverable; } + /// /// @brief Discover devices of this type available (for configuration). /// @note Mainly used for network devices. Allows to find devices, e.g. via ssdp, mDNS or cloud ways. @@ -436,7 +442,7 @@ public slots: /// Timer object which makes sure that LED data is written at a minimum rate /// e.g. some devices will switch off when they do not receive data at least every 15 seconds - QScopedPointer _refreshTimer; + QScopedPointer _refreshTimer; // Device configuration parameters @@ -516,7 +522,7 @@ private slots: void stopRefreshTimer(); /// Timer that enables a device (used to retry enablement, if enabled failed before) - QScopedPointer _enableAttemptsTimer; + QScopedPointer _enableAttemptsTimer; // Device configuration parameters diff --git a/include/leddevice/LedDeviceWrapper.h b/include/leddevice/LedDeviceWrapper.h index 1b495a41f..52a2a9896 100644 --- a/include/leddevice/LedDeviceWrapper.h +++ b/include/leddevice/LedDeviceWrapper.h @@ -7,6 +7,7 @@ #include #include +#include class LedDevice; class Hyperion; @@ -21,7 +22,7 @@ class LedDeviceWrapper : public QObject { Q_OBJECT public: - explicit LedDeviceWrapper(Hyperion* hyperion); + explicit LedDeviceWrapper(const QSharedPointer& hyperionInstance); ~LedDeviceWrapper() override; /// /// @brief Constructs a new LedDevice, moves to thread and starts @@ -158,11 +159,11 @@ private slots: /// The common Logger instance for all LED-devices Logger * _log; - // parent Hyperion - Hyperion* _hyperion; + /// Hyperion instance pointer + QWeakPointer _hyperionWeak; // Pointer to the current LED-device & its thread - QScopedPointer _ledDevice; + QScopedPointer _ledDevice; QScopedPointer _ledDeviceThread; // LED-Device's states diff --git a/include/utils/Components.h b/include/utils/Components.h index 841604a9a..73b5d9483 100644 --- a/include/utils/Components.h +++ b/include/utils/Components.h @@ -44,7 +44,7 @@ inline const char* componentToString(Components c) case COMP_SMOOTHING: return "Smoothing"; case COMP_BLACKBORDER: return "Blackborder detector"; #if defined(ENABLE_FORWARDER) - case COMP_FORWARDER: return "Json/Proto forwarder"; + case COMP_FORWARDER: return "JSON-/Flatbuffer forwarder"; #endif #if defined(ENABLE_BOBLIGHT_SERVER) case COMP_BOBLIGHTSERVER:return "Boblight server"; diff --git a/include/utils/Logger.h b/include/utils/Logger.h index 016b63525..49a3fe107 100644 --- a/include/utils/Logger.h +++ b/include/utils/Logger.h @@ -17,18 +17,21 @@ // stl includes #ifdef _WIN32 + #include #endif #include +// ================================================================ + #define LOG_MESSAGE(severity, logger, ...) (logger)->Message(severity, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) // standard log messages -#define Debug(logger, ...) LOG_MESSAGE(Logger::DEBUG , logger, __VA_ARGS__) -#define Info(logger, ...) LOG_MESSAGE(Logger::INFO , logger, __VA_ARGS__) -#define Warning(logger, ...) LOG_MESSAGE(Logger::WARNING, logger, __VA_ARGS__) -#define Error(logger, ...) LOG_MESSAGE(Logger::ERRORR , logger, __VA_ARGS__) +#define Debug(logger, ...) LOG_MESSAGE(Logger::LOG_DEBUG , logger, __VA_ARGS__) +#define Info(logger, ...) LOG_MESSAGE(Logger::LOG_INFO , logger, __VA_ARGS__) +#define Warning(logger, ...) LOG_MESSAGE(Logger::LOG_WARNING, logger, __VA_ARGS__) +#define Error(logger, ...) LOG_MESSAGE(Logger::LOG_ERROR , logger, __VA_ARGS__) // conditional log messages #define DebugIf(condition, logger, ...) if (condition) Debug(logger, __VA_ARGS__) @@ -43,13 +46,14 @@ class Logger : public QObject Q_OBJECT public: + enum LogLevel { - UNSET = 0, - DEBUG = 1, - INFO = 2, - WARNING = 3, - ERRORR = 4, - OFF = 5 + LOG_UNSET = 0, + LOG_DEBUG = 1, + LOG_INFO = 2, + LOG_WARNING = 3, + LOG_ERROR = 4, + LOG_OFF = 5 }; struct T_LOG_MESSAGE @@ -65,7 +69,7 @@ class Logger : public QObject QString levelString; }; - static Logger* getInstance(const QString & name = "", const QString & subName = "__", LogLevel minLevel=Logger::INFO); + static Logger* getInstance(const QString & name = "", const QString & subName = "__", LogLevel minLevel= Logger::LogLevel::LOG_INFO); static void deleteInstance(const QString & name = "", const QString & subName = "__"); static void setLogLevel(LogLevel level, const QString & name = "", const QString & subName = "__"); static LogLevel getLogLevel(const QString & name = "", const QString & subName = "__"); @@ -80,7 +84,7 @@ class Logger : public QObject void newLogMessage(Logger::T_LOG_MESSAGE); protected: - Logger(const QString & name="", const QString & subName = "__", LogLevel minLevel = INFO); + Logger(const QString & name="", const QString & subName = "__", LogLevel minLevel = Logger::LogLevel::LOG_INFO); ~Logger() override; private: @@ -123,7 +127,7 @@ class LoggerManager : public QObject public slots: void handleNewLogMessage(const Logger::T_LOG_MESSAGE&); - QJsonArray getLogMessageBuffer(Logger::LogLevel filter=Logger::UNSET) const; + QJsonArray getLogMessageBuffer(Logger::LogLevel filter= Logger::LogLevel::LOG_UNSET) const; signals: void newLogMessage(const Logger::T_LOG_MESSAGE&); diff --git a/include/utils/NetOrigin.h b/include/utils/NetOrigin.h index 648d3de3c..9d88a724f 100644 --- a/include/utils/NetOrigin.h +++ b/include/utils/NetOrigin.h @@ -19,14 +19,6 @@ class NetOrigin : public QObject NetOrigin(QObject* parent = nullptr, Logger* log = Logger::getInstance("NETWORK")); public: - /// - /// @brief Check if address is allowed to connect. The local address is the network adapter ip this connection comes from - /// @param address The peer address - /// @param local The local address of the socket (Differs based on NetworkAdapter IP or localhost) - /// @return True when allowed, else false - /// - bool accessAllowed(const QHostAddress& address, const QHostAddress& local) const; - /// /// @brief Check if address is in subnet of local /// @return True or false @@ -36,21 +28,6 @@ class NetOrigin : public QObject static NetOrigin *getInstance() { return instance; } static NetOrigin *instance; -private slots: - /// - /// @brief Handle settings update from SettingsManager - /// @param type settingyType from enum - /// @param config configuration object - /// - void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - private: Logger* _log; - /// True when internet access is allowed - bool _isInternetAccessAllowed; - /// True when internet access is restricted by a white list - bool _isInternetAccessRestricted; - /// Whitelisted ip addresses - QList _ipWhitelist; - }; diff --git a/include/utils/NetUtils.h b/include/utils/NetUtils.h index 9fe0e0139..df35cdcf7 100644 --- a/include/utils/NetUtils.h +++ b/include/utils/NetUtils.h @@ -227,14 +227,22 @@ inline bool resolveHostToAddress(Logger* log, const QString& hostname, QHostAddr QMdnsEngine::Record const service = resolveMdDnsServiceRecord(hostname.toUtf8()); if (!service.target().isEmpty()) { - Info(log, "Resolved service [%s] to mDNS hostname [%s], service port [%d]", QSTRING_CSTR(hostname), service.target().constData(), service.port()); - target = service.target(); - port = service.port(); + if (!service.target().isEmpty()) + { + Info(log, "Resolved service [%s] to mDNS hostname [%s], service port [%d]", QSTRING_CSTR(hostname), service.target().constData(), service.port()); + target = service.target(); + port = service.port(); + } + else + { + Error(log, "Failed to resolved service [%s] to an mDNS hostname", QSTRING_CSTR(hostname)); + return false; + } } else { Error(log, "Cannot resolve mDNS hostname for given service [%s]!", QSTRING_CSTR(hostname)); - return areHostAddressPartOK; + return false; } } #endif diff --git a/include/utils/TrackedMemory.h b/include/utils/TrackedMemory.h index ecb7e2aff..e0fa804dd 100644 --- a/include/utils/TrackedMemory.h +++ b/include/utils/TrackedMemory.h @@ -9,30 +9,27 @@ #include #include -#define ENABLE_MEMORY_TRACKING 0 +#define ENABLE_MEMORY_TRACKING 1 #define USE_TRACKED_SHARED_PTR ENABLE_MEMORY_TRACKING -#define USE_TRACKED_DELETE_LATER ENABLE_MEMORY_TRACKING +#define USE_TRACKED_CUSTOM_DELETE ENABLE_MEMORY_TRACKING #if USE_TRACKED_SHARED_PTR #define MAKE_TRACKED_SHARED(T, ...) makeTrackedShared(__VA_ARGS__) #else - #define MAKE_TRACKED_SHARED(T, ...) QSharedPointer(new T(__VA_ARGS__), &QObject::deleteLater) + #define MAKE_TRACKED_SHARED(T, ...) QSharedPointer(new T(__VA_ARGS__), &customDelete) #endif -#if USE_TRACKED_DELETE_LATER - #define DELETE_LATER_FN(T) trackedDeleteLater -#else - #define DELETE_LATER_FN(T) &QObject::deleteLater -#endif +// Custom Delete function templates -// Deleter function template template -void trackedDeleteLater(T* ptr) +void customDelete(T* ptr) { if (!ptr) return; +#if USE_TRACKED_CUSTOM_DELETE + QString subComponent = "__"; QString typeName; @@ -51,27 +48,45 @@ void trackedDeleteLater(T* ptr) } Logger* log = Logger::getInstance("MEMORY", subComponent); - - Debug(log, "Deleting object of type '%s' at %p", QSTRING_CSTR(typeName), static_cast(ptr)); + Debug(log, "Deleting object of type '%s' at %p - current thread: '%s'", QSTRING_CSTR(typeName), static_cast(ptr), QSTRING_CSTR(QThread::currentThread()->objectName())); +#endif if constexpr (std::is_base_of::value) { QThread* thread = ptr->thread(); - if (thread && thread->isRunning()) - { + if (thread && thread == QThread::currentThread()) { +#if USE_TRACKED_CUSTOM_DELETE + Debug(log, "QObject<%s> deleted immediately (current thread: '%s').", QSTRING_CSTR(typeName), QSTRING_CSTR(thread->objectName())); +#endif ptr->deleteLater(); - Debug(log, "QObject<%s>::deleteLater() scheduled on thread '%s'", QSTRING_CSTR(typeName), QSTRING_CSTR(thread->objectName())); } else { - delete ptr; - Debug(log, "QObject<%s> deleted immediately (thread not running).", QSTRING_CSTR(typeName)); + if (thread && thread->isRunning()) + { + // Schedule deleteLater from the object's thread +#if USE_TRACKED_CUSTOM_DELETE + Debug(log, "QObject<%s>::deleteLater() scheduled via invokeMethod on thread '%s'", QSTRING_CSTR(typeName), QSTRING_CSTR(thread->objectName())); +#endif + QMetaObject::invokeMethod(ptr, "deleteLater", Qt::QueuedConnection); + } + else + { + // This should be an *extremely rare* fallback and indicates a bug in the thread shutdown sequence. +#if USE_TRACKED_CUSTOM_DELETE + Debug(log, "<%s> object's owning thread is not running. Deleted immediately (thread not running) - current thread: '%s'", QSTRING_CSTR(typeName), QSTRING_CSTR(QThread::currentThread()->objectName())); +#endif + delete ptr; + } } } else { +#if USE_TRACKED_CUSTOM_DELETE + Debug(log, "Non-QObject<%s> deleted immediately - current thread: '%s'.", QSTRING_CSTR(typeName), QSTRING_CSTR(QThread::currentThread()->objectName())); +#endif delete ptr; - Debug(log, "Non-QObject<%s> deleted immediately.", QSTRING_CSTR(typeName)); + } } @@ -80,6 +95,8 @@ template QSharedPointer makeTrackedShared(Args&&... args) { T* rawPtr = new T(std::forward(args)...); + if (!rawPtr) + return QSharedPointer(); QString subComponent = "__"; QString typeName; @@ -99,9 +116,9 @@ QSharedPointer makeTrackedShared(Args&&... args) } Logger* log = Logger::getInstance("MEMORY", subComponent); - Debug(log, "Creating object of type '%s' at %p", QSTRING_CSTR(typeName), static_cast(rawPtr)); + Debug(log, "Creating object of type '%s' at %p (current thread '%s')", QSTRING_CSTR(typeName), static_cast(rawPtr), QSTRING_CSTR(QThread::currentThread()->objectName())); - return QSharedPointer(rawPtr, DELETE_LATER_FN(T)); + return QSharedPointer(rawPtr, &customDelete); } #endif // TRACKEDMEMORY_H diff --git a/include/utils/WaitTime.h b/include/utils/WaitTime.h index e6140f01f..49b8f81df 100644 --- a/include/utils/WaitTime.h +++ b/include/utils/WaitTime.h @@ -8,22 +8,28 @@ inline void wait(std::chrono::milliseconds millisecondsWait) { - QEventLoop loop; - QTimer timer; - timer.setTimerType(Qt::PreciseTimer); - QTimer::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - timer.start(millisecondsWait.count()); - loop.exec(); + if (millisecondsWait.count() > 0) + { + QEventLoop loop; + QTimer timer; + timer.setTimerType(Qt::PreciseTimer); + QTimer::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(millisecondsWait.count()); + loop.exec(); + } } inline void wait(int millisecondsWait) { - QEventLoop loop; - QTimer timer; - timer.setTimerType(Qt::PreciseTimer); - QTimer::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - timer.start(millisecondsWait); - loop.exec(); + if (millisecondsWait > 0) + { + QEventLoop loop; + QTimer timer; + timer.setTimerType(Qt::PreciseTimer); + QTimer::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.start(millisecondsWait); + loop.exec(); + } } #endif // WAITTIME_H diff --git a/libsrc/api/API.cpp b/libsrc/api/API.cpp index d2ea5b2ad..38e3aef5f 100644 --- a/libsrc/api/API.cpp +++ b/libsrc/api/API.cpp @@ -41,7 +41,7 @@ const int IMAGE_SCALE = 2000; API::API(Logger *log, bool localConnection, QObject *parent) : QObject(parent), _currInstanceIndex (NO_INSTANCE_ID) - , _hyperion (nullptr) + , _hyperionWeak(nullptr) , _authorized (false) , _adminAuthorized (false) , _localConnection(localConnection) @@ -104,7 +104,11 @@ void API::setColor(int priority, const std::vector &ledColors, int time fledColors.emplace_back(ColorRgb{ledColors[i], ledColors[i + 1], ledColors[i + 2]}); } - QMetaObject::invokeMethod(_hyperion.get(), "setColor", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(std::vector, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "setColor", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(std::vector, fledColors), Q_ARG(int, timeout_ms), Q_ARG(QString, origin)); + } } } @@ -194,8 +198,12 @@ bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &reply Image image(data.width, data.height); memcpy(image.memptr(), data.data.data(), static_cast(data.data.size())); - QMetaObject::invokeMethod(_hyperion.get(), "registerInput", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName)); - QMetaObject::invokeMethod(_hyperion.get(), "setInputImage", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(Image, image), Q_ARG(int64_t, data.duration)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "registerInput", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(hyperion::Components, comp), Q_ARG(QString, data.origin), Q_ARG(QString, data.imgName)); + QMetaObject::invokeMethod(hyperion.get(), "setInputImage", Qt::QueuedConnection, Q_ARG(int, data.priority), Q_ARG(Image, image), Q_ARG(int64_t, data.duration)); + } return true; } @@ -204,7 +212,11 @@ bool API::clearPriority(int priority, QString &replyMsg, hyperion::Components /* { if (priority < 0 || (priority > 0 && priority < PriorityMuxer::BG_PRIORITY)) { - QMetaObject::invokeMethod(_hyperion.get(), "clear", Qt::QueuedConnection, Q_ARG(int, priority)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "clear", Qt::QueuedConnection, Q_ARG(int, priority)); + } } else { @@ -220,7 +232,11 @@ bool API::setComponentState(const QString &comp, bool &compState, QString &reply if (component != COMP_INVALID) { - QMetaObject::invokeMethod(_hyperion.get(), "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(hyperion::Components, component), Q_ARG(bool, compState)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "compStateChangeRequest", Qt::QueuedConnection, Q_ARG(hyperion::Components, component), Q_ARG(bool, compState)); + } return true; } replyMsg = QString("Unknown component name: %1").arg(comp); @@ -229,25 +245,37 @@ bool API::setComponentState(const QString &comp, bool &compState, QString &reply void API::setLedMappingType(int type, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion.get(), "setLedMappingType", Qt::QueuedConnection, Q_ARG(int, type)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "setLedMappingType", Qt::QueuedConnection, Q_ARG(int, type)); + } } void API::setVideoMode(VideoMode mode, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion.get(), "setVideoMode", Qt::QueuedConnection, Q_ARG(VideoMode, mode)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "setVideoMode", Qt::QueuedConnection, Q_ARG(VideoMode, mode)); + } } #if defined(ENABLE_EFFECTENGINE) bool API::setEffect(const EffectCmdData &dat, hyperion::Components /*callerComp*/) { int isStarted; - if (!dat.args.isEmpty()) + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) { - QMetaObject::invokeMethod(_hyperion.get(), "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(QJsonObject, dat.args), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.pythonScript), Q_ARG(QString, dat.origin), Q_ARG(QString, dat.data)); - } - else - { - QMetaObject::invokeMethod(_hyperion.get(), "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin)); + if (!dat.args.isEmpty()) + { + QMetaObject::invokeMethod(hyperion.get(), "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(QJsonObject, dat.args), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.pythonScript), Q_ARG(QString, dat.origin), Q_ARG(QString, dat.data)); + } + else + { + QMetaObject::invokeMethod(hyperion.get(), "setEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isStarted), Q_ARG(QString, dat.effectName), Q_ARG(int, dat.priority), Q_ARG(int, dat.duration), Q_ARG(QString, dat.origin)); + } } return isStarted >= 0; @@ -256,12 +284,20 @@ bool API::setEffect(const EffectCmdData &dat, hyperion::Components /*callerComp* void API::setSourceAutoSelect(bool state, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion.get(), "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(bool, state)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "setSourceAutoSelect", Qt::QueuedConnection, Q_ARG(bool, state)); + } } void API::setVisiblePriority(int priority, hyperion::Components /*callerComp*/) { - QMetaObject::invokeMethod(_hyperion.get(), "setVisiblePriority", Qt::QueuedConnection, Q_ARG(int, priority)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "setVisiblePriority", Qt::QueuedConnection, Q_ARG(int, priority)); + } } void API::registerInput(int priority, hyperion::Components component, const QString &origin, const QString &owner, hyperion::Components callerComp) @@ -273,7 +309,11 @@ void API::registerInput(int priority, hyperion::Components component, const QStr _activeRegisters.insert({priority, registerData{component, origin, owner, callerComp}}); - QMetaObject::invokeMethod(_hyperion.get(), "registerInput", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "registerInput", Qt::QueuedConnection, Q_ARG(int, priority), Q_ARG(hyperion::Components, component), Q_ARG(QString, origin), Q_ARG(QString, owner)); + } } void API::unregisterInput(int priority) @@ -291,12 +331,14 @@ bool API::setHyperionInstance(quint8 inst) return true; } - if (_hyperion.get() != nullptr) + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) { - disconnect(_hyperion.get(), nullptr, this, nullptr); + disconnect(hyperion.get(), nullptr, this, nullptr); } - QMetaObject::invokeMethod(_instanceManager, "getHyperionInstance", Qt::DirectConnection, Q_RETURN_ARG(QSharedPointer, _hyperion), Q_ARG(quint8, inst)); + QMetaObject::invokeMethod(_instanceManager, "getHyperionInstance", Qt::DirectConnection, Q_RETURN_ARG(QSharedPointer, hyperion), Q_ARG(quint8, inst)); + _hyperionWeak = hyperion; _currInstanceIndex = inst; return true; @@ -305,7 +347,11 @@ bool API::setHyperionInstance(quint8 inst) bool API::isHyperionEnabled() { int isEnabled; - QMetaObject::invokeMethod(_hyperion.get(), "isComponentEnabled", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isEnabled), Q_ARG(hyperion::Components, hyperion::COMP_ALL)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "isComponentEnabled", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, isEnabled), Q_ARG(hyperion::Components, hyperion::COMP_ALL)); + } return isEnabled > 0; } @@ -373,7 +419,11 @@ QString API::deleteEffect(const QString &name) if (_adminAuthorized) { QString res; - QMetaObject::invokeMethod(_hyperion.get(), "deleteEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QString, name)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "deleteEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QString, name)); + } return res; } return NO_AUTHORIZATION; @@ -384,7 +434,11 @@ QString API::saveEffect(const QJsonObject &data) if (_adminAuthorized) { QString res; - QMetaObject::invokeMethod(_hyperion.get(), "saveEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QJsonObject, data)); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QMetaObject::invokeMethod(hyperion.get(), "saveEffect", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, res), Q_ARG(QJsonObject, data)); + } return res; } return NO_AUTHORIZATION; diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 9acba4001..3fc3e3e4b 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -90,7 +90,7 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject qRegisterMetaType("Event"); - connect(EventHandler::getInstance().data(), &EventHandler::signalEvent, [log, this](const Event &event) { + connect(EventHandler::getInstance().data(), &EventHandler::signalEvent, this, [log, this](const Event &event) { if (event == Event::Quit) { _isServiceAvailable = false; @@ -499,15 +499,16 @@ void JsonAPI::handleCommand(const JsonApiCommand& cmd, const QJsonObject &messag void JsonAPI::handleGetImageSnapshotCommand(const QJsonObject &message, const JsonApiCommand &cmd) { - if (_hyperion.isNull()) + if (_hyperionWeak.isNull()) { sendErrorReply("Failed to create snapshot. No instance provided.", cmd); return; } + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); QString replyMsg; QString const imageFormat = message["format"].toString("PNG"); - const PriorityMuxer::InputInfo priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority()); + const PriorityMuxer::InputInfo priorityInfo = hyperion->getPriorityInfo(hyperion->getCurrentPriority()); Image image = priorityInfo.image; QImage snapshot(reinterpret_cast(image.memptr()), image.width(), image.height(), qsizetype(3) * image.width(), QImage::Format_RGB888); QByteArray byteArray; @@ -532,12 +533,14 @@ void JsonAPI::handleGetImageSnapshotCommand(const QJsonObject &message, const Js void JsonAPI::handleGetLedSnapshotCommand(const QJsonObject& /*message*/, const JsonApiCommand &cmd) { - if (_hyperion.isNull()) + if (_hyperionWeak.isNull()) { sendErrorReply("Failed to create snapshot. No instance not.", cmd); return; } + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + std::vector ledColors; QEventLoop loop; QTimer timer; @@ -551,7 +554,7 @@ void JsonAPI::handleGetLedSnapshotCommand(const QJsonObject& /*message*/, const // Capture LED colors when the LED data signal is emitted (execute only once) std::unique_ptr context{new QObject}; QObject* pcontext = context.get(); - QObject::connect(_hyperion.get(), &Hyperion::ledDeviceData, pcontext, + QObject::connect(hyperion.get(), &Hyperion::ledDeviceData, pcontext, [this, &loop, context = std::move(context), &ledColors, cmd](std::vector ledColorsUpdate) mutable { ledColors = ledColorsUpdate; loop.quit(); // Ensure the event loop quits immediately when data is received @@ -700,20 +703,21 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const JsonApiC switch (cmd.getSubCommand()) { case SubCommand::Empty: case SubCommand::GetInfo: + { // Global information - info = JsonInfo::getInfo(_hyperion.get(),_log); - + info = JsonInfo::getInfo(_hyperionWeak.toStrongRef(), _log); + if (!_noListener && message.contains("subscribe")) { - const QJsonArray &subscriptions = message["subscribe"].toArray(); + const QJsonArray& subscriptions = message["subscribe"].toArray(); QStringList const invaliCommands = _jsonCB->subscribe(subscriptions); if (!invaliCommands.isEmpty()) { - errorDetails.append("subscribe - Invalid commands provided: " + invaliCommands.join(',')); + errorDetails.append("subscribe - Invalid commands provided: " + invaliCommands.join(',')); } } // END - + } break; case SubCommand::Subscribe: @@ -782,24 +786,28 @@ void JsonAPI::handleClearallCommand(const QJsonObject &message, const JsonApiCom void JsonAPI::handleAdjustmentCommand(const QJsonObject &message, const JsonApiCommand& cmd) { - const QJsonObject &adjustment = message["adjustment"].toObject(); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + const QJsonObject& adjustment = message["adjustment"].toObject(); - const QList adjustmentIds = _hyperion->getAdjustmentIds(); - if (adjustmentIds.isEmpty()) { - sendErrorReply("No adjustment data available", cmd); - return; - } + const QList adjustmentIds = hyperion->getAdjustmentIds(); + if (adjustmentIds.isEmpty()) { + sendErrorReply("No adjustment data available", cmd); + return; + } - const QString adjustmentId = adjustment["id"].toString(adjustmentIds.first()); - ColorAdjustment *colorAdjustment = _hyperion->getAdjustment(adjustmentId); - if (colorAdjustment == nullptr) { - Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str()); - return; - } + const QString adjustmentId = adjustment["id"].toString(adjustmentIds.first()); + ColorAdjustment* colorAdjustment = hyperion->getAdjustment(adjustmentId); + if (colorAdjustment == nullptr) { + Warning(_log, "Incorrect adjustment identifier: %s", adjustmentId.toStdString().c_str()); + return; + } - applyColorAdjustments(adjustment, colorAdjustment); - applyTransforms(adjustment, colorAdjustment); - _hyperion->adjustmentsUpdated(); + applyColorAdjustments(adjustment, colorAdjustment); + applyTransforms(adjustment, colorAdjustment); + hyperion->adjustmentsUpdated(); + } sendSuccessReply(cmd); } @@ -1202,13 +1210,16 @@ void JsonAPI::handleLedColorsCommand(const QJsonObject& /*message*/, const JsonA { switch (cmd.subCommand) { case SubCommand::LedStreamStart: - _jsonCB->subscribe( Subscription::LedColorsUpdate); - if (!_hyperion.isNull()) + { + _jsonCB->subscribe(Subscription::LedColorsUpdate); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) { // push once - _hyperion->update(); + hyperion->update(); } sendSuccessReply(cmd); + } break; case SubCommand::LedStreamStop: @@ -1879,7 +1890,11 @@ void JsonAPI::sendNewRequest(const QJsonValue &infoData, const QString &command, if (instanceCmdType != InstanceCmd::No) { - request["instance"] = _hyperion->getInstanceIndex(); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + request["instance"] = hyperion->getInstanceIndex(); + } } request["info"] = infoData; @@ -1901,7 +1916,7 @@ void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instanceID, quint8 const currentInstance = _currInstanceIndex; if (instanceID == currentInstance) { - _hyperion = _instanceManager->getHyperionInstance(instanceID); + _hyperionWeak = _instanceManager->getHyperionInstance(instanceID); _jsonCB->setSubscriptionsTo(instanceID); } } @@ -1909,8 +1924,6 @@ void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instanceID, case InstanceState::H_STOPPED: { - //Release reference to stopped Hyperion instance - _hyperion.clear(); } break; diff --git a/libsrc/api/JsonCallbacks.cpp b/libsrc/api/JsonCallbacks.cpp index 9d1f5ca33..a6c7ce06a 100644 --- a/libsrc/api/JsonCallbacks.cpp +++ b/libsrc/api/JsonCallbacks.cpp @@ -22,10 +22,10 @@ using namespace hyperion; JsonCallbacks::JsonCallbacks(Logger *log, const QString& peerAddress, QObject* parent) : QObject(parent) , _log (log) - , _hyperion(nullptr) + , _hyperionWeak(nullptr) , _peerAddress (peerAddress) - , _componentRegister(nullptr) - , _prioMuxer(nullptr) + , _componentRegisterWeak(nullptr) + , _prioMuxerWeak(nullptr) , _islogMsgStreamingActive(false) { qRegisterMetaType("InputsMap"); @@ -42,11 +42,6 @@ void JsonCallbacks::handleInstanceStateChange(InstanceState state, quint8 instan if (instanceID == _instanceID) { setSubscriptionsTo(NO_INSTANCE_ID); - - //Release reference to stopped Hyperion instance - _prioMuxer.clear(); - _componentRegister.clear(); - _hyperion.clear(); } } @@ -55,7 +50,7 @@ void JsonCallbacks::handleInstanceStateChange(InstanceState state, quint8 instan case InstanceState::H_STARTED: case InstanceState::H_CREATED: case InstanceState::H_DELETED: - break; + break; } } @@ -66,14 +61,17 @@ bool JsonCallbacks::subscribe(const Subscription::Type cmd) return true; } + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + switch (cmd) { // Global subscriptions -#if defined(ENABLE_EFFECTENGINE) case Subscription::EffectsUpdate: +#if defined(ENABLE_EFFECTENGINE) connect(EffectFileHandler::getInstance(), &EffectFileHandler::effectListChanged, this, &JsonCallbacks::handleEffectListChange); - break; #endif + break; + case Subscription::EventUpdate: connect(EventHandler::getInstance().data(), &EventHandler::signalEvent, this, &JsonCallbacks::handleEventUpdate); break; @@ -98,43 +96,49 @@ bool JsonCallbacks::subscribe(const Subscription::Type cmd) // Instance specific subscriptions case Subscription::AdjustmentUpdate: - if (!_hyperion.isNull()) { - connect(_hyperion.get(), &Hyperion::adjustmentChanged, this, &JsonCallbacks::handleAdjustmentChange); + if (!hyperion.isNull()) { + connect(hyperion.get(), &Hyperion::adjustmentChanged, this, &JsonCallbacks::handleAdjustmentChange); } break; case Subscription::ComponentsUpdate: - if (!_componentRegister.isNull()) { - connect(_componentRegister.get(), &ComponentRegister::updatedComponentState, this, &JsonCallbacks::handleComponentState); + { + QSharedPointer componentRegisterStrong = _componentRegisterWeak.toStrongRef(); + if (!componentRegisterStrong.isNull()) { + connect(componentRegisterStrong.get(), &ComponentRegister::updatedComponentState, this, &JsonCallbacks::handleComponentState); } + } break; case Subscription::ImageToLedMappingUpdate: - if (!_hyperion.isNull()) { - connect(_hyperion.get(), &Hyperion::imageToLedsMappingChanged, this, &JsonCallbacks::handleImageToLedsMappingChange); + if (!hyperion.isNull()) { + connect(hyperion.get(), &Hyperion::imageToLedsMappingChanged, this, &JsonCallbacks::handleImageToLedsMappingChange); } break; case Subscription::ImageUpdate: - if (!_hyperion.isNull()) { - connect(_hyperion.get(), &Hyperion::currentImage, this, &JsonCallbacks::handleImageUpdate); + if (!hyperion.isNull()) { + connect(hyperion.get(), &Hyperion::currentImage, this, &JsonCallbacks::handleImageUpdate); } break; case Subscription::LedColorsUpdate: - if (!_hyperion.isNull()) { - connect(_hyperion.get(), &Hyperion::rawLedColors, this, &JsonCallbacks::handleLedColorUpdate); + if (!hyperion.isNull()) { + connect(hyperion.get(), &Hyperion::rawLedColors, this, &JsonCallbacks::handleLedColorUpdate); } break; case Subscription::LedsUpdate: - if (!_hyperion.isNull()) { - connect(_hyperion.get(), &Hyperion::settingsChanged, this, &JsonCallbacks::handleLedsConfigChange); + if (!hyperion.isNull()) { + connect(hyperion.get(), &Hyperion::settingsChanged, this, &JsonCallbacks::handleLedsConfigChange); } break; case Subscription::PrioritiesUpdate: - if (!_prioMuxer.isNull()) { - connect(_prioMuxer.get(), &PriorityMuxer::prioritiesChanged, this, &JsonCallbacks::handlePriorityUpdate); + { + QSharedPointer prioMuxerStrong = _prioMuxerWeak.toStrongRef(); + if (!prioMuxerStrong.isNull()) { + connect(prioMuxerStrong.get(), &PriorityMuxer::prioritiesChanged, this, &JsonCallbacks::handlePriorityUpdate); } + } break; case Subscription::VideomodeUpdate: - if (!_hyperion.isNull()) { - connect(_hyperion.get(), &Hyperion::newVideoMode, this, &JsonCallbacks::handleVideoModeChange); + if (!hyperion.isNull()) { + connect(hyperion.get(), &Hyperion::newVideoMode, this, &JsonCallbacks::handleVideoModeChange); } break; @@ -194,14 +198,16 @@ bool JsonCallbacks::unsubscribe(const Subscription::Type cmd) _subscribedCommands.remove(cmd); - switch (cmd) { + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + switch (cmd) { // Global subscriptions -#if defined(ENABLE_EFFECTENGINE) case Subscription::EffectsUpdate: +#if defined(ENABLE_EFFECTENGINE) disconnect(EffectFileHandler::getInstance(), &EffectFileHandler::effectListChanged, this, &JsonCallbacks::handleEffectListChange); - break; #endif + break; + case Subscription::EventUpdate: disconnect(EventHandler::getInstance().data(), &EventHandler::signalEvent, this, &JsonCallbacks::handleEventUpdate); break; @@ -225,44 +231,50 @@ bool JsonCallbacks::unsubscribe(const Subscription::Type cmd) // Instance specific subscriptions case Subscription::AdjustmentUpdate: - if (!_hyperion.isNull()) { - disconnect(_hyperion.get(), &Hyperion::adjustmentChanged, this, &JsonCallbacks::handleAdjustmentChange); + if (!hyperion.isNull()) { + disconnect(hyperion.get(), &Hyperion::adjustmentChanged, this, &JsonCallbacks::handleAdjustmentChange); } break; case Subscription::ComponentsUpdate: - if (!_componentRegister.isNull()) { - disconnect(_componentRegister.get(), &ComponentRegister::updatedComponentState, this, &JsonCallbacks::handleComponentState); + { + QSharedPointer componentRegisterStrong = _componentRegisterWeak.toStrongRef(); + if (!componentRegisterStrong.isNull()) { + disconnect(componentRegisterStrong.get(), &ComponentRegister::updatedComponentState, this, &JsonCallbacks::handleComponentState); } + } break; case Subscription::ImageToLedMappingUpdate: - if (!_hyperion.isNull()) { - disconnect(_hyperion.get(), &Hyperion::imageToLedsMappingChanged, this, &JsonCallbacks::handleImageToLedsMappingChange); + if (!hyperion.isNull()) { + disconnect(hyperion.get(), &Hyperion::imageToLedsMappingChanged, this, &JsonCallbacks::handleImageToLedsMappingChange); } break; case Subscription::ImageUpdate: - if (!_hyperion.isNull()) { - disconnect(_hyperion.get(), &Hyperion::currentImage, this, &JsonCallbacks::handleImageUpdate); + if (!hyperion.isNull()) { + disconnect(hyperion.get(), &Hyperion::currentImage, this, &JsonCallbacks::handleImageUpdate); } break; case Subscription::LedColorsUpdate: - if (!_hyperion.isNull()) { - disconnect(_hyperion.get(), &Hyperion::rawLedColors, this, &JsonCallbacks::handleLedColorUpdate); + if (!hyperion.isNull()) { + disconnect(hyperion.get(), &Hyperion::rawLedColors, this, &JsonCallbacks::handleLedColorUpdate); } break; case Subscription::LedsUpdate: - if (!_hyperion.isNull()) { - disconnect(_hyperion.get(), &Hyperion::settingsChanged, this, &JsonCallbacks::handleLedsConfigChange); + if (!hyperion.isNull()) { + disconnect(hyperion.get(), &Hyperion::settingsChanged, this, &JsonCallbacks::handleLedsConfigChange); } break; case Subscription::PrioritiesUpdate: - if (!_prioMuxer.isNull()) { - disconnect(_prioMuxer.get(), &PriorityMuxer::prioritiesChanged, this, &JsonCallbacks::handlePriorityUpdate); + { + QSharedPointer prioMuxerStrong = _prioMuxerWeak.toStrongRef(); + if (!prioMuxerStrong.isNull()) { + disconnect(prioMuxerStrong.get(), &PriorityMuxer::prioritiesChanged, this, &JsonCallbacks::handlePriorityUpdate); } + } break; case Subscription::VideomodeUpdate: - if (!_hyperion.isNull()) { - disconnect(_hyperion.get(), &Hyperion::newVideoMode, this, &JsonCallbacks::handleVideoModeChange); + if (!hyperion.isNull()) { + disconnect(hyperion.get(), &Hyperion::newVideoMode, this, &JsonCallbacks::handleVideoModeChange); } break; @@ -323,13 +335,14 @@ void JsonCallbacks::setSubscriptionsTo(quint8 instanceID) resetSubscriptions(); _instanceID = instanceID; + // update pointer QSharedPointer const hyperion = HyperionIManager::getInstance()->getHyperionInstance(instanceID); - if (!hyperion.isNull() && hyperion != _hyperion) + if (!hyperion.isNull() && hyperion != _hyperionWeak) { - _hyperion = hyperion; - _componentRegister = _hyperion->getComponentRegister(); - _prioMuxer = _hyperion->getMuxerInstance(); + _hyperionWeak = hyperion; + _componentRegisterWeak = hyperion->getComponentRegister(); + _prioMuxerWeak = hyperion->getMuxerInstance(); } // re-apply subscriptions @@ -421,7 +434,7 @@ void JsonCallbacks::handlePriorityUpdate(int currentPriority, const PriorityMuxe { QJsonObject data; data["priorities"] = JsonInfo::getPrioritiestInfo(currentPriority, activeInputs); - data["priorities_autoselect"] = _hyperion->sourceAutoSelectEnabled(); + data["priorities_autoselect"] = _hyperionWeak.toStrongRef()->sourceAutoSelectEnabled(); doCallback(Subscription::PrioritiesUpdate, data); } @@ -436,7 +449,7 @@ void JsonCallbacks::handleImageToLedsMappingChange(int mappingType) void JsonCallbacks::handleAdjustmentChange() { - doCallback(Subscription::AdjustmentUpdate, JsonInfo::getAdjustmentInfo(_hyperion.get(),_log)); + doCallback(Subscription::AdjustmentUpdate, JsonInfo::getAdjustmentInfo(_hyperionWeak.toStrongRef(), _log)); } void JsonCallbacks::handleVideoModeChange(VideoMode mode) diff --git a/libsrc/api/JsonInfo.cpp b/libsrc/api/JsonInfo.cpp index c62a9a430..190d50690 100644 --- a/libsrc/api/JsonInfo.cpp +++ b/libsrc/api/JsonInfo.cpp @@ -20,7 +20,7 @@ #include #endif -QJsonObject JsonInfo::getInfo(const Hyperion* hyperion, Logger* log) +QJsonObject JsonInfo::getInfo(const QSharedPointer& hyperionInstance, Logger* log) { QJsonObject info {}; @@ -32,22 +32,22 @@ QJsonObject JsonInfo::getInfo(const Hyperion* hyperion, Logger* log) info["effects"] = JsonInfo::getEffects(); // Global/Instance specific information - info["grabbers"] = JsonInfo::getGrabbers(hyperion); - info["components"] = JsonInfo::getComponents(hyperion); + info["grabbers"] = JsonInfo::getGrabbers(hyperionInstance); + info["components"] = JsonInfo::getComponents(hyperionInstance); // Instance specific information - info["priorities"] = JsonInfo::getPrioritiestInfo(hyperion); - info["adjustment"] = JsonInfo::getAdjustmentInfo(hyperion, log); - info["activeLedColor"] = JsonInfo::getActiveColors(hyperion); + info["priorities"] = JsonInfo::getPrioritiestInfo(hyperionInstance); + info["adjustment"] = JsonInfo::getAdjustmentInfo(hyperionInstance, log); + info["activeLedColor"] = JsonInfo::getActiveColors(hyperionInstance); #if defined(ENABLE_EFFECTENGINE) - info["activeEffects"] = JsonInfo::getActiveEffects(hyperion); + info["activeEffects"] = JsonInfo::getActiveEffects(hyperionInstance); #endif - if (hyperion != nullptr) + if (!hyperionInstance.isNull()) { - info["priorities_autoselect"] = hyperion->sourceAutoSelectEnabled(); - info["videomode"] = QString(videoMode2String(hyperion->getCurrentVideoMode())); - info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(hyperion->getLedMappingType()); - info["leds"] = hyperion->getSetting(settings::LEDS).array(); + info["priorities_autoselect"] = hyperionInstance->sourceAutoSelectEnabled(); + info["videomode"] = QString(videoMode2String(hyperionInstance->getCurrentVideoMode())); + info["imageToLedMappingType"] = ImageProcessor::mappingTypeToStr(hyperionInstance->getLedMappingType()); + info["leds"] = hyperionInstance->getSetting(settings::LEDS).array(); } else { @@ -57,23 +57,23 @@ QJsonObject JsonInfo::getInfo(const Hyperion* hyperion, Logger* log) info["leds"] = QJsonArray(); } - // BEGIN | The following entries are deprecated but used to ensure backward compatibility with hyperion Classic or up to Hyperion 2.0.16 + // BEGIN | The following entries are deprecated but used to ensure backward compatibility with hyperionInstance Classic or up to hyperionInstance 2.0.16 info["hostname"] = QHostInfo::localHostName(); - info["transform"] = JsonInfo::getTransformationInfo(hyperion); + info["transform"] = JsonInfo::getTransformationInfo(hyperionInstance); return info; } -QJsonArray JsonInfo::getAdjustmentInfo(const Hyperion* hyperion, Logger* log) +QJsonArray JsonInfo::getAdjustmentInfo(const QSharedPointer& hyperionInstance, Logger* log) { - if (hyperion == nullptr) + if (hyperionInstance.isNull()) { return QJsonArray(); } QJsonArray adjustmentArray; - for (const QString &adjustmentId : hyperion->getAdjustmentIds()) + for (const QString &adjustmentId : hyperionInstance->getAdjustmentIds()) { - const ColorAdjustment *colorAdjustment = hyperion->getAdjustment(adjustmentId); + const ColorAdjustment *colorAdjustment = hyperionInstance->getAdjustment(adjustmentId); if (colorAdjustment == nullptr) { Error(log, "Incorrect color adjustment id: %s", QSTRING_CSTR(adjustmentId)); @@ -143,14 +143,14 @@ QJsonArray JsonInfo::getAdjustmentInfo(const Hyperion* hyperion, Logger* log) return adjustmentArray; } -QJsonArray JsonInfo::getPrioritiestInfo(const Hyperion* hyperion) +QJsonArray JsonInfo::getPrioritiestInfo(const QSharedPointer& hyperionInstance) { - if (hyperion == nullptr) + if (hyperionInstance.isNull()) { return QJsonArray(); } - return getPrioritiestInfo(hyperion->getCurrentPriority(), hyperion->getPriorityInfo()); + return getPrioritiestInfo(hyperionInstance->getCurrentPriority(), hyperionInstance->getPriorityInfo()); } QJsonArray JsonInfo::getPrioritiestInfo(int currentPriority, const PriorityMuxer::InputsMap& activeInputs) @@ -310,14 +310,14 @@ QJsonArray JsonInfo::getAvailableAudioGrabbers() return availableAudioGrabbers; } -QJsonObject JsonInfo::getGrabbers(const Hyperion* hyperion) +QJsonObject JsonInfo::getGrabbers(const QSharedPointer& hyperionInstance) { QJsonObject grabbers; quint8 idx { NO_INSTANCE_ID }; - if (hyperion != nullptr) + if (!hyperionInstance.isNull()) { - idx = hyperion->getInstanceIndex(); + idx = hyperionInstance->getInstanceIndex(); } // SCREEN @@ -425,15 +425,15 @@ QJsonArray JsonInfo::getServices() return services; } -QJsonArray JsonInfo::getComponents(const Hyperion* hyperion) +QJsonArray JsonInfo::getComponents(const QSharedPointer& hyperionInstance) { // get available components QJsonArray component; std::map components; - if (hyperion!= nullptr) + if (!hyperionInstance.isNull()) { - components = hyperion->getComponentRegister()->getRegister(); + components = hyperionInstance->getComponentRegister()->getRegister(); } else { @@ -466,17 +466,17 @@ QJsonArray JsonInfo::getInstanceInfo() return instanceInfo; } -QJsonArray JsonInfo::getTransformationInfo(const Hyperion* hyperion) +QJsonArray JsonInfo::getTransformationInfo(const QSharedPointer& hyperionInstance) { // TRANSFORM INFORMATION (DEFAULT VALUES) QJsonArray transformArray; - if (hyperion == nullptr) + if (hyperionInstance.isNull()) { return transformArray; } - for (const QString &transformId : hyperion->getAdjustmentIds()) + for (const QString &transformId : hyperionInstance->getAdjustmentIds()) { QJsonObject transform; QJsonArray blacklevel; @@ -509,18 +509,18 @@ QJsonArray JsonInfo::getTransformationInfo(const Hyperion* hyperion) return transformArray; } -QJsonArray JsonInfo::getActiveEffects(const Hyperion* hyperion) +QJsonArray JsonInfo::getActiveEffects(const QSharedPointer& hyperionInstance) { // ACTIVE EFFECT INFO QJsonArray activeEffects; - if (hyperion == nullptr) + if (hyperionInstance.isNull()) { return activeEffects; } #if defined(ENABLE_EFFECTENGINE) - for (const ActiveEffectDefinition &activeEffectDefinition : hyperion->getActiveEffects()) + for (const ActiveEffectDefinition &activeEffectDefinition : hyperionInstance->getActiveEffects()) { if (activeEffectDefinition.priority != PriorityMuxer::LOWEST_PRIORITY - 1) { @@ -537,12 +537,13 @@ QJsonArray JsonInfo::getActiveEffects(const Hyperion* hyperion) return activeEffects; } -QJsonArray JsonInfo::getActiveColors(const Hyperion* hyperion) +QJsonArray JsonInfo::getActiveColors(const QSharedPointer& hyperionInstance) { // ACTIVE STATIC LED COLOR QJsonArray activeLedColors; - if (hyperion == nullptr) + QSharedPointer hyperion = hyperionInstance; + if (hyperion.isNull()) { return activeLedColors; } diff --git a/libsrc/blackborder/BlackBorderProcessor.cpp b/libsrc/blackborder/BlackBorderProcessor.cpp index ab13a49c6..e34b73413 100644 --- a/libsrc/blackborder/BlackBorderProcessor.cpp +++ b/libsrc/blackborder/BlackBorderProcessor.cpp @@ -8,9 +8,9 @@ using namespace hyperion; -BlackBorderProcessor::BlackBorderProcessor(Hyperion* hyperion, QObject* parent) +BlackBorderProcessor::BlackBorderProcessor(const QSharedPointer& hyperionInstance, QObject* parent) : QObject(parent) - , _hyperion(hyperion) + , _hyperionWeak(hyperionInstance) , _enabled(false) , _unknownSwitchCnt(600) , _borderSwitchCnt(50) @@ -18,31 +18,45 @@ BlackBorderProcessor::BlackBorderProcessor(Hyperion* hyperion, QObject* parent) , _blurRemoveCnt(1) , _detectionMode("default") , _detector(nullptr) - , _currentBorder({true, -1, -1}) - , _previousDetectedBorder({true, -1, -1}) + , _currentBorder({ true, -1, -1 }) + , _previousDetectedBorder({ true, -1, -1 }) , _consistentCnt(0) , _inconsistentCnt(10) , _oldThreshold(-0.1) , _hardDisabled(false) , _userEnabled(false) { - // init - handleSettingsUpdate(settings::BLACKBORDER, _hyperion->getSetting(settings::BLACKBORDER)); + QString subComponent{ "__" }; - // listen for settings updates - connect(_hyperion, &Hyperion::settingsChanged, this, &BlackBorderProcessor::handleSettingsUpdate); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + _log = Logger::getInstance("BLACKBORDER", subComponent); + + // init + handleSettingsUpdate(settings::BLACKBORDER, _hyperionWeak.toStrongRef()->getSetting(settings::BLACKBORDER)); - // listen for component state changes - connect(_hyperion, &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest); + // listen for settings updates + connect(hyperion.get(), &Hyperion::settingsChanged, this, &BlackBorderProcessor::handleSettingsUpdate); + // listen for component state changes + connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest); + } _detector = std::make_unique(_oldThreshold); } +BlackBorderProcessor::~BlackBorderProcessor() +{ + qDebug() << "BlackBorderProcessor::~BlackBorderProcessor()..."; +} + void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { if(type == settings::BLACKBORDER) { - if (_hyperion->isComponentEnabled(COMP_BLACKBORDER) == -1) + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (!hyperion.isNull() && hyperion->isComponentEnabled(COMP_BLACKBORDER) == -1) { //Disable, if service is not available _enabled = false; @@ -64,7 +78,7 @@ void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJson _detector = std::make_unique(_oldThreshold); } - Debug(Logger::getInstance("BLACKBORDER", "I"+QString::number(_hyperion->getInstanceIndex())), "Set mode to: %s", QSTRING_CSTR(_detectionMode)); + Debug(_log, "Set mode to: %s", QSTRING_CSTR(_detectionMode)); // eval the comp state handleCompStateChangeRequest(hyperion::COMP_BLACKBORDER, obj["enable"].toBool(true)); @@ -88,7 +102,11 @@ void BlackBorderProcessor::handleCompStateChangeRequest(hyperion::Components com _enabled = enable; } - _hyperion->setNewComponentState(hyperion::COMP_BLACKBORDER, enable); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + hyperion->setNewComponentState(hyperion::COMP_BLACKBORDER, enable); + } } } diff --git a/libsrc/boblightserver/BoblightClientConnection.cpp b/libsrc/boblightserver/BoblightClientConnection.cpp index 242a2bb83..53acec275 100644 --- a/libsrc/boblightserver/BoblightClientConnection.cpp +++ b/libsrc/boblightserver/BoblightClientConnection.cpp @@ -33,21 +33,33 @@ namespace { const int BOBLIGHT_MAX_PRIORITY = PriorityMuxer::BG_PRIORITY-1; } //End of constants -BoblightClientConnection::BoblightClientConnection(Hyperion* hyperion, QTcpSocket* socket, int priority) +BoblightClientConnection::BoblightClientConnection(const QSharedPointer& hyperionInstance, QTcpSocket* socket, int priority) : QObject() , _locale(QLocale::C) , _socket(socket) - , _imageProcessor(hyperion->getImageProcessor()) - , _hyperion(hyperion) + , _hyperionWeak(hyperionInstance) + , _imageProcessorWeak() , _receiveBuffer() , _priority(priority) - , _ledColors(hyperion->getLedCount(), ColorRgb::BLACK) - , _log(Logger::getInstance("BOBLIGHT")) + , _ledColors() + , _log() , _clientAddress(QHostInfo::fromName(socket->peerAddress().toString()).hostName()) { // initalize the locale. Start with the default C-locale _locale.setNumberOptions(QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator); + QString subComponent{ "__" }; + + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + + _imageProcessorWeak = hyperion->getImageProcessor(); + _ledColors.resize(hyperion->getLedCount(), ColorRgb::BLACK); + } + _log = Logger::getInstance("BOBLIGHT", subComponent); + // connect internal signals and slots connect(_socket, &QTcpSocket::disconnected, this, &BoblightClientConnection::socketClosed); connect(_socket, &QTcpSocket::readyRead, this, &BoblightClientConnection::readData); @@ -113,7 +125,11 @@ void BoblightClientConnection::socketClosed() { if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) { - _hyperion->clear(_priority); + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + hyperion->clear(_priority); + } } emit connectionClosed(this); @@ -125,6 +141,7 @@ void BoblightClientConnection::handleMessage(const QString& message) QStringList messageParts = QStringUtils::split(message, ' ', QStringUtils::SplitBehavior::SkipEmptyParts); if (!messageParts.isEmpty()) { + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); if (messageParts[0] == "hello") { sendMessage("hello\n"); @@ -179,7 +196,7 @@ void BoblightClientConnection::handleMessage(const QString& message) // send current color values to hyperion if this is the last led assuming leds values are send in order of id if (ledIndex == _ledColors.size() - 1) { - _hyperion->setInput(_priority, _ledColors); + hyperion->setInput(_priority, _ledColors); } return; @@ -201,11 +218,11 @@ void BoblightClientConnection::handleMessage(const QString& message) const int prio = static_cast(parseUInt(messageParts[2], &rc)); if (rc) { - int currentPriority = _hyperion->getCurrentPriority(); + int currentPriority = hyperion->getCurrentPriority(); if (prio == currentPriority) { - Error(_log, "The priority %i is already in use onther component of type [%s]", prio, componentToString(_hyperion->getPriorityInfo(currentPriority).componentId)); + Error(_log, "The priority %i is already in use onther component of type [%s]", prio, componentToString(hyperion->getPriorityInfo(currentPriority).componentId)); _socket->close(); } else @@ -213,7 +230,7 @@ void BoblightClientConnection::handleMessage(const QString& message) if (prio < BOBLIGHT_MIN_PRIORITY || prio > BOBLIGHT_MAX_PRIORITY) { _priority = BOBLIGHT_DEFAULT_PRIORITY; - while (_hyperion->getActivePriorities().contains(_priority)) + while (hyperion->getActivePriorities().contains(_priority)) { _priority += 1; } @@ -222,12 +239,12 @@ void BoblightClientConnection::handleMessage(const QString& message) Warning(_log, "The priority %i is not in the priority range of [%d-%d]. Priority %i is used instead.", prio, BOBLIGHT_MIN_PRIORITY, BOBLIGHT_MAX_PRIORITY, _priority); // register new priority (previously modified) - _hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress)); + hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress)); } else { // register new priority - _hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress)); + hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress)); _priority = prio; } } @@ -239,16 +256,16 @@ void BoblightClientConnection::handleMessage(const QString& message) { if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) { - int currentPriority = _hyperion->getCurrentPriority(); + int currentPriority = hyperion->getCurrentPriority(); if ( _priority != currentPriority) { // register this connection's priority - _hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress)); + hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress)); } if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) { - _hyperion->setInput(_priority, _ledColors); // send current color values to hyperion + hyperion->setInput(_priority, _ledColors); // send current color values to hyperion } } @@ -409,14 +426,23 @@ void BoblightClientConnection::sendLightMessage() { char buffer[256]; - int n = snprintf(buffer, sizeof(buffer), "lights %d\n", _hyperion->getLedCount()); - sendMessage(QByteArray(buffer, n)); - - double h0, h1, v0, v1; - for (int i = 0; i < _hyperion->getLedCount(); ++i) + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) { - _imageProcessor->getScanParameters(i, h0, h1, v0, v1); - n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100 * v0, 100 * v1, 100 * h0, 100 * h1); + int n = snprintf(buffer, sizeof(buffer), "lights %d\n", hyperion->getLedCount()); sendMessage(QByteArray(buffer, n)); + + QSharedPointer const imageProcessor = _imageProcessorWeak.toStrongRef(); + if (imageProcessor) + { + double h0, h1, v0, v1; + + for (int i = 0; i < hyperion->getLedCount(); ++i) + { + imageProcessor->getScanParameters(i, h0, h1, v0, v1); + n = snprintf(buffer, sizeof(buffer), "light %03d scan %f %f %f %f\n", i, 100 * v0, 100 * v1, 100 * h0, 100 * h1); + sendMessage(QByteArray(buffer, n)); + } + } } } diff --git a/libsrc/boblightserver/BoblightClientConnection.h b/libsrc/boblightserver/BoblightClientConnection.h index b2e69d149..8a83b5cd7 100644 --- a/libsrc/boblightserver/BoblightClientConnection.h +++ b/libsrc/boblightserver/BoblightClientConnection.h @@ -29,7 +29,7 @@ class BoblightClientConnection : public QObject /// @param socket The Socket object for this connection /// @param hyperion The Hyperion server /// - BoblightClientConnection(Hyperion* hyperion, QTcpSocket * socket, int priority); + explicit BoblightClientConnection(const QSharedPointer& hyperionInstance, QTcpSocket * socket, int priority); /// /// Destructor @@ -125,10 +125,10 @@ private slots: QTcpSocket * _socket; /// The processor for translating images to led-values - ImageProcessor * _imageProcessor; + QWeakPointer _imageProcessorWeak; /// Link to Hyperion for writing led-values to a priority channel - Hyperion * _hyperion; + QWeakPointer _hyperionWeak; /// The buffer used for reading data from the socket QByteArray _receiveBuffer; diff --git a/libsrc/boblightserver/BoblightServer.cpp b/libsrc/boblightserver/BoblightServer.cpp index 1cd413456..9f6e05f23 100644 --- a/libsrc/boblightserver/BoblightServer.cpp +++ b/libsrc/boblightserver/BoblightServer.cpp @@ -16,22 +16,29 @@ using namespace hyperion; -BoblightServer::BoblightServer(Hyperion* hyperion,const QJsonDocument& config) - : QObject() - , _hyperion(hyperion) +BoblightServer::BoblightServer(const QSharedPointer& hyperionInstance,const QJsonDocument& config) + : _hyperionWeak(hyperionInstance) , _server(new QTcpServer(this)) , _openConnections() , _priority(0) , _log(nullptr) , _port(0) { - QString subComponent = _hyperion->property("instance").toString(); - _log= Logger::getInstance("BOBLIGHT", subComponent); + QString subComponent{ "__" }; + + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + + // listen for component change + connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &BoblightServer::compStateChangeRequest); + } + _log = Logger::getInstance("BOBLIGHT", subComponent); + Debug(_log, "Instance created"); - // listen for component change - connect(_hyperion, &Hyperion::compStateChangeRequest, this, &BoblightServer::compStateChangeRequest); // listen new connection signal from server connect(_server, &QTcpServer::newConnection, this, &BoblightServer::newConnection); @@ -41,7 +48,7 @@ BoblightServer::BoblightServer(Hyperion* hyperion,const QJsonDocument& config) BoblightServer::~BoblightServer() { - stop(); + qDebug() << "BoblightServer::~BoblightServer()..."; } void BoblightServer::start() @@ -54,7 +61,11 @@ void BoblightServer::start() Info(_log, "Boblight service started on port: %d", _port); - _hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening()); + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening()); + } } void BoblightServer::stop() @@ -68,7 +79,12 @@ void BoblightServer::stop() _server->close(); Info(_log, "Boblight service stopped"); - _hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening()); + + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening()); + } } bool BoblightServer::active() const @@ -99,7 +115,7 @@ void BoblightServer::newConnection() if (socket != nullptr) { Info(_log, "New connection from %s ", QSTRING_CSTR(QString("Boblight@%1").arg(socket->peerAddress().toString()))); - BoblightClientConnection * connection = new BoblightClientConnection(_hyperion, socket, _priority); + BoblightClientConnection * connection = new BoblightClientConnection(_hyperionWeak.toStrongRef(), socket, _priority); _openConnections.insert(connection); // register slot for cleaning up after the connection closed diff --git a/libsrc/cec/CECHandler.cpp b/libsrc/cec/CECHandler.cpp index cfbcc3959..f7fd5835a 100644 --- a/libsrc/cec/CECHandler.cpp +++ b/libsrc/cec/CECHandler.cpp @@ -36,11 +36,6 @@ CECHandler::CECHandler(const QJsonDocument& config, QObject * parent) _cecConfig.callbackParam = this; } -CECHandler::~CECHandler() -{ - Info(_logger, "CEC handler stopped"); -} - void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { if(type == settings::CECEVENTS) @@ -124,6 +119,7 @@ void CECHandler::stop() _cecAdapter->Close(); UnloadLibCec(_cecAdapter); } + Info(_logger, "CEC handler stopped"); } bool CECHandler::enable() diff --git a/libsrc/db/DBMigrationManager.cpp b/libsrc/db/DBMigrationManager.cpp index 44c7ab00b..d454dbf77 100644 --- a/libsrc/db/DBMigrationManager.cpp +++ b/libsrc/db/DBMigrationManager.cpp @@ -99,6 +99,8 @@ bool DBMigrationManager::upgradeGlobalSettings(const semver::version& currentVer upgradeGlobalSettings_2_0_16(migratedVersion, config); //Migration step for versions < 2.0.17 upgradeGlobalSettings_2_1_0(migratedVersion, config); + //Migration step for versions < 2.1.1 + upgradeGlobalSettings_2_1_2(migratedVersion, config); // Set the daqtabase version to the current build version QJsonObject generalConfig = config["general"].toObject(); @@ -436,6 +438,32 @@ bool DBMigrationManager::upgradeGlobalSettings_2_1_0(semver::version& currentVer return migrated; } +bool DBMigrationManager::upgradeGlobalSettings_2_1_2(semver::version& currentVersion, QJsonObject& config) +{ + bool migrated = false; + const semver::version targetVersion{ "2.1.2" }; + + if (currentVersion < targetVersion) + { + Info(_log, "Global settings: Migrate from version [%s] to version [%s] or later", currentVersion.getVersion().c_str(), targetVersion.getVersion().c_str()); + currentVersion = targetVersion; + + if (config.contains("network")) + { + QJsonObject newNetworkConfig = config["network"].toObject(); + newNetworkConfig.remove("internetAccessAPI"); + newNetworkConfig.remove("restirctedInternetAccessAPI"); + newNetworkConfig.remove("ipWhitelist"); + config.insert("network", newNetworkConfig); + + Debug(_log, "Network settings migrated"); + migrated = true; + } + } + + return migrated; +} + bool DBMigrationManager::upgradeInstanceSettings_alpha_9(semver::version& currentVersion, quint8 instance, QJsonObject& config) { bool migrated = false; diff --git a/libsrc/effectengine/Effect.cpp b/libsrc/effectengine/Effect.cpp index ad0745e84..8ccbb6d78 100644 --- a/libsrc/effectengine/Effect.cpp +++ b/libsrc/effectengine/Effect.cpp @@ -19,9 +19,9 @@ namespace { int DEFAULT_MAX_UPDATE_RATE_HZ { 200 }; } //End of constants -Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &script, const QString &name, const QJsonObject &args, const QString &imageData) +Effect::Effect(const QSharedPointer& hyperionInstance, int priority, int timeout, const QString &script, const QString &name, const QJsonObject &args, const QString &imageData) : QThread() - , _hyperion(hyperion) + , _hyperionWeak(hyperionInstance) , _priority(priority) , _timeout(timeout) , _isEndless(timeout <= PriorityMuxer::ENDLESS) @@ -31,16 +31,26 @@ Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &scr , _imageData(imageData) , _endTime(-1) , _interupt(false) - , _imageSize(hyperion->getLedGridSize()) - , _image(_imageSize,QImage::Format_ARGB32_Premultiplied) + , _imageSize() + , _image() , _lowestUpdateIntervalInSeconds(1/static_cast(DEFAULT_MAX_UPDATE_RATE_HZ)) { - _colors.resize(_hyperion->getLedCount()); - _colors.fill(ColorRgb::BLACK); + QString subComponent{ "__" }; + + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instane").toString(); + _colors.resize(hyperion->getLedCount()); + _imageSize = hyperion->getLedGridSize(); + } - _log = Logger::getInstance("EFFECTENGINE"); + _log = Logger::getInstance("EFFECTENGINE", subComponent); + + _colors.fill(ColorRgb::BLACK); // init effect image for image based effects, size is based on led layout + _image = QImage(_imageSize, QImage::Format_ARGB32_Premultiplied); _image.fill(Qt::black); _painter = new QPainter(&_image); @@ -49,6 +59,7 @@ Effect::Effect(Hyperion *hyperion, int priority, int timeout, const QString &scr Effect::~Effect() { + qDebug() << "Effect::~Effect()..."; delete _painter; _imageStack.clear(); } @@ -89,9 +100,11 @@ bool Effect::setModuleParameters() return false; } + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + // Add ledCount variable to the interpreter int ledCount = 0; - QMetaObject::invokeMethod(_hyperion, "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, ledCount)); + QMetaObject::invokeMethod(hyperion.get(), "getLedCount", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, ledCount)); PyObject* ledCountObj = Py_BuildValue("i", ledCount); if (PyObject_SetAttrString(module, "ledCount", ledCountObj) < 0) { PyErr_Print(); // Print error if setting attribute fails @@ -100,7 +113,7 @@ bool Effect::setModuleParameters() // Add minimumWriteTime variable to the interpreter int latchTime = 0; - QMetaObject::invokeMethod(_hyperion, "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, latchTime)); + QMetaObject::invokeMethod(hyperion.get(), "getLatchTime", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, latchTime)); PyObject* latchTimeObj = Py_BuildValue("i", latchTime); if (PyObject_SetAttrString(module, "latchTime", latchTimeObj) < 0) { PyErr_Print(); // Print error if setting attribute fails diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index e66fa371c..29a7bdff2 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -18,39 +18,49 @@ #include #include "HyperionConfig.h" -EffectEngine::EffectEngine(Hyperion * hyperion) - : _hyperion(hyperion) +EffectEngine::EffectEngine(const QSharedPointer& hyperionInstance) + : _hyperionWeak(hyperionInstance) , _log(nullptr) , _effectFileHandler(EffectFileHandler::getInstance()) { - QString subComponent = hyperion->property("instance").toString(); - _log= Logger::getInstance("EFFECTENGINE", subComponent); + QString subComponent{ "__" }; + + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + } + _log = Logger::getInstance("EFFECTENGINE", subComponent); + Q_INIT_RESOURCE(EffectEngine); qRegisterMetaType("hyperion::Components"); - // connect the Hyperion channel clear feedback - connect(_hyperion, &Hyperion::channelCleared, this, &EffectEngine::channelCleared); - connect(_hyperion, &Hyperion::allChannelsCleared, this, &EffectEngine::allChannelsCleared); + if (hyperion) + { + // connect the Hyperion channel clear feedback + connect(hyperion.get(), &Hyperion::channelCleared, this, &EffectEngine::channelCleared); + connect(hyperion.get(), &Hyperion::allChannelsCleared, this, &EffectEngine::allChannelsCleared); - // get notifications about refreshed effect list - connect(_effectFileHandler, &EffectFileHandler::effectListChanged, this, &EffectEngine::handleUpdatedEffectList); + // get notifications about refreshed effect list + connect(_effectFileHandler, &EffectFileHandler::effectListChanged, this, &EffectEngine::handleUpdatedEffectList); - // Stop all effects when instance is disabled, restart the same when enabled - connect(_hyperion, &Hyperion::compStateChangeRequest, this, [=](hyperion::Components component, bool enable) { - if (component == hyperion::COMP_ALL) - { - if (enable) + // Stop all effects when instance is disabled, restart the same when enabled + connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, [=](hyperion::Components component, bool enable) { + if (component == hyperion::COMP_ALL) { - startCachedEffects(); + if (enable) + { + startCachedEffects(); + } + else + { + cacheRunningEffects(); + stopAllEffects(); + } } - else - { - cacheRunningEffects(); - stopAllEffects(); - } - } - }); + }); + } // register smooth cfgs and fill available effects handleUpdatedEffectList(); @@ -58,6 +68,7 @@ EffectEngine::EffectEngine(Hyperion * hyperion) EffectEngine::~EffectEngine() { + qDebug() << "EffectEngine::~EffectEngine()..."; } std::list EffectEngine::getActiveEffects() const @@ -109,8 +120,9 @@ void EffectEngine::handleUpdatedEffectList() { _availableEffects.clear(); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); //Add smoothing config entry to support dynamic effects done in configurator - _hyperion->updateSmoothingConfig(SmoothingConfigID::EFFECT_DYNAMIC); + hyperion->updateSmoothingConfig(SmoothingConfigID::EFFECT_DYNAMIC); unsigned specificId = SmoothingConfigID::EFFECT_SPECIFIC; for (auto def : _effectFileHandler->getEffects()) @@ -124,7 +136,7 @@ void EffectEngine::handleUpdatedEffectList() Debug(_log, "Effect \"%s\": Add custom smoothing settings [%d]. Type: Linear, Settling time: %dms, Interval: %.fHz ", QSTRING_CSTR(def.name), specificId, settlingTime_ms, ledUpdateFrequency_hz); - def.smoothCfg = _hyperion->updateSmoothingConfig( + def.smoothCfg = hyperion->updateSmoothingConfig( ++specificId, settlingTime_ms, ledUpdateFrequency_hz, @@ -164,7 +176,7 @@ int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args, Debug(_log, "Effect \"%s\": Apply dynamic smoothing settings, if smoothing. Type: Linear, Settling time: %dms, Interval: %.fHz ", QSTRING_CSTR(effectName), settlingTime_ms, ledUpdateFrequency_hz); - smoothCfg = _hyperion->updateSmoothingConfig( + smoothCfg = _hyperionWeak.toStrongRef()->updateSmoothingConfig( SmoothingConfigID::EFFECT_DYNAMIC, settlingTime_ms, ledUpdateFrequency_hz, @@ -203,15 +215,16 @@ int EffectEngine::runEffectScript(const QString &script, const QString &name, co channelCleared(priority); // create the effect - Effect *effect = new Effect(_hyperion, priority, timeout, script, name, args, imageData); - connect(effect, &Effect::setInput, _hyperion, &Hyperion::setInput, Qt::QueuedConnection); - connect(effect, &Effect::setInputImage, _hyperion, &Hyperion::setInputImage, Qt::QueuedConnection); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + Effect *effect = new Effect(hyperion, priority, timeout, script, name, args, imageData); + connect(effect, &Effect::setInput, hyperion.get(), &Hyperion::setInput, Qt::QueuedConnection); + connect(effect, &Effect::setInputImage, hyperion.get(), &Hyperion::setInputImage, Qt::QueuedConnection); connect(effect, &QThread::finished, this, &EffectEngine::effectFinished); _activeEffects.push_back(effect); // start the effect Debug(_log, "Start the effect: \"%s\"", QSTRING_CSTR(name)); - _hyperion->registerInput(priority, hyperion::COMP_EFFECT, origin, name ,smoothCfg); + hyperion->registerInput(priority, hyperion::COMP_EFFECT, origin, name ,smoothCfg); effect->start(); return 0; diff --git a/libsrc/effectengine/EffectModule.cpp b/libsrc/effectengine/EffectModule.cpp index c6ba1881f..6abf2205b 100644 --- a/libsrc/effectengine/EffectModule.cpp +++ b/libsrc/effectengine/EffectModule.cpp @@ -85,73 +85,78 @@ PyObject* EffectModule::json2python(const QJsonValue& jsonData) { switch (jsonData.type()) { - case QJsonValue::Null: - Py_RETURN_NONE; + case QJsonValue::Null: + Py_RETURN_NONE; - case QJsonValue::Undefined: - Py_RETURN_NOTIMPLEMENTED; + case QJsonValue::Undefined: + Py_RETURN_NOTIMPLEMENTED; - case QJsonValue::Double: - { - double value = jsonData.toDouble(); - if (value == static_cast(value)) // If no fractional part, value is equal to its integer representation + case QJsonValue::Double: { - return Py_BuildValue("i", static_cast(value)); + double value = jsonData.toDouble(); + if (value == static_cast(value)) // If no fractional part, value is equal to its integer representation + { + return Py_BuildValue("i", static_cast(value)); + } + return Py_BuildValue("d", value); } - return Py_BuildValue("d", value); - } - case QJsonValue::Bool: - return PyBool_FromLong(jsonData.toBool() ? 1 : 0); + case QJsonValue::Bool: + return PyBool_FromLong(jsonData.toBool() ? 1 : 0); - case QJsonValue::String: - return PyUnicode_FromString(jsonData.toString().toUtf8().constData()); + case QJsonValue::String: + return PyUnicode_FromString(jsonData.toString().toUtf8().constData()); - case QJsonValue::Array: - { - QJsonArray arrayData = jsonData.toArray(); - PyObject* list = PyList_New(arrayData.size()); - int index = 0; - for (QJsonArray::iterator i = arrayData.begin(); i != arrayData.end(); ++i, ++index) + case QJsonValue::Array: { - PyObject* obj = json2python(*i); - Py_INCREF(obj); - PyList_SetItem(list, index, obj); - Py_XDECREF(obj); + QJsonArray arrayData = jsonData.toArray(); + PyObject* list = PyList_New(arrayData.size()); + int index = 0; + for (QJsonArray::iterator i = arrayData.begin(); i != arrayData.end(); ++i, ++index) + { + PyObject* obj = json2python(*i); + if (!obj) + { + Py_XDECREF(list); + return nullptr; // Error occurred, return null + } + PyList_SetItem(list, index, obj); + } + return list; } - return list; - } - case QJsonValue::Object: { - // Python's dict - QJsonObject jsonObject = jsonData.toObject(); - PyObject* pyDict = PyDict_New(); - for (auto it = jsonObject.begin(); it != jsonObject.end(); ++it) { - // Convert key - PyObject* pyKey = PyUnicode_FromString(it.key().toUtf8().constData()); - if (!pyKey) { - Py_XDECREF(pyDict); - return nullptr; // Error occurred, return null - } - // Convert value - PyObject* pyValue = json2python(it.value()); - if (!pyValue) { - Py_XDECREF(pyKey); - Py_XDECREF(pyDict); - return nullptr; // Error occurred, return null + case QJsonValue::Object: + { + // Python's dict + QJsonObject jsonObject = jsonData.toObject(); + PyObject* pyDict = PyDict_New(); + for (auto it = jsonObject.begin(); it != jsonObject.end(); ++it) + { + // Convert key + PyObject* pyKey = PyUnicode_FromString(it.key().toUtf8().constData()); + if (!pyKey) + { + Py_XDECREF(pyDict); + return nullptr; // Error occurred, return null + } + // Convert value + PyObject* pyValue = json2python(it.value()); + if (!pyValue) + { + Py_XDECREF(pyKey); + Py_XDECREF(pyDict); + return nullptr; // Error occurred, return null + } + // Add to dictionary + PyDict_SetItem(pyDict, pyKey, pyValue); } - // Add to dictionary - PyDict_SetItem(pyDict, pyKey, pyValue); - Py_XDECREF(pyKey); - Py_XDECREF(pyValue); + return pyDict; } - return pyDict; - } - default: - // Unsupported type - PyErr_SetString(PyExc_TypeError, "Unsupported QJsonValue type."); - return nullptr; + default: + // Unsupported type + PyErr_SetString(PyExc_TypeError, "Unsupported QJsonValue type."); + return nullptr; } assert(false); @@ -214,7 +219,7 @@ PyObject* EffectModule::wrapSetColor(PyObject* self, PyObject* args) if (PyByteArray_Check(bytearray)) { size_t length = PyByteArray_Size(bytearray); - if (length == 3 * static_cast(getEffect()->_hyperion->getLedCount())) + if (length == 3 * static_cast(getEffect()->_hyperionWeak.toStrongRef()->getLedCount())) { char* data = PyByteArray_AS_STRING(bytearray); memcpy(getEffect()->_colors.data(), data, length); @@ -349,6 +354,7 @@ PyObject* EffectModule::wrapGetImage(PyObject* self, PyObject* args) if (reader.canRead()) { PyObject* result = PyList_New(reader.imageCount()); + if(!result) return nullptr; for (int i = 0; i < reader.imageCount(); ++i) { @@ -363,6 +369,7 @@ PyObject* EffectModule::wrapGetImage(PyObject* self, PyObject* args) { if (cropLeft + cropRight >= width || cropTop + cropBottom >= height) { + Py_DECREF(result); QString errorStr = QString("Rejecting invalid crop values: left: %1, right: %2, top: %3, bottom: %4, higher than height/width %5/%6").arg(cropLeft).arg(cropRight).arg(cropTop).arg(cropBottom).arg(height).arg(width); PyErr_SetString(PyExc_RuntimeError, qPrintable(errorStr)); return nullptr; @@ -373,22 +380,42 @@ PyObject* EffectModule::wrapGetImage(PyObject* self, PyObject* args) height = qimage.height(); } - QByteArray binaryImage; - for (int i = 0; i < height; i++) + qsizetype size = qsizetype(width) * qsizetype(height) * 3; + QByteArray binaryImage(size, Qt::Uninitialized); + char* dest = binaryImage.data(); + for (int i = 0; i < height; ++i) { const QRgb* scanline = reinterpret_cast(qimage.scanLine(i)); - const QRgb* end = scanline + qimage.width(); - for (; scanline != end; scanline++) + for (int j = 0; j < width; ++j) { - binaryImage.append(!grayscale ? (char)qRed(scanline[0]) : (char)qGray(scanline[0])); - binaryImage.append(!grayscale ? (char)qGreen(scanline[1]) : (char)qGray(scanline[1])); - binaryImage.append(!grayscale ? (char)qBlue(scanline[2]) : (char)qGray(scanline[2])); + QRgb pixel = scanline[j]; + *dest++ = static_cast(!grayscale ? qRed(pixel) : qGray(pixel)); + *dest++ = static_cast(!grayscale ? qGreen(pixel) : qGray(pixel)); + *dest++ = static_cast(!grayscale ? qBlue(pixel) : qGray(pixel)); } } - PyList_SET_ITEM(result, i, Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", PyByteArray_FromStringAndSize(binaryImage.constData(), binaryImage.size()))); + + PyObject* pyBytes = PyByteArray_FromStringAndSize(binaryImage.constData(), binaryImage.size()); + if (!pyBytes) + { + Py_DECREF(result); + return nullptr; + } + + PyObject* dict = Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", pyBytes); + Py_DECREF(pyBytes); + + if (!dict) + { + Py_DECREF(result); + return nullptr; + } + + PyList_SET_ITEM(result, i, dict); } else { + Py_DECREF(result); PyErr_SetString(PyExc_TypeError, reader.errorString().toUtf8().constData()); return nullptr; } diff --git a/libsrc/flatbufserver/FlatBufferServer.cpp b/libsrc/flatbufserver/FlatBufferServer.cpp index c33412ed2..a0caf6222 100644 --- a/libsrc/flatbufserver/FlatBufferServer.cpp +++ b/libsrc/flatbufserver/FlatBufferServer.cpp @@ -75,24 +75,19 @@ void FlatBufferServer::newConnection() { if(QTcpSocket* socket = _server->nextPendingConnection()) { - if(_netOrigin->accessAllowed(socket->peerAddress(), socket->localAddress())) - { - Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); - FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this); - - client->setPixelDecimation(_pixelDecimation); - - // internal - connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected); - connect(client, &FlatBufferClient::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput); - connect(client, &FlatBufferClient::clearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput); - connect(client, &FlatBufferClient::setGlobalInputImage, GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage); - connect(client, &FlatBufferClient::setGlobalInputColor, GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor); - connect(client, &FlatBufferClient::setBufferImage, GlobalSignals::getInstance(), &GlobalSignals::setBufferImage); - _openConnections.append(client); - } - else - socket->close(); + Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); + FlatBufferClient *client = new FlatBufferClient(socket, _timeout, this); + + client->setPixelDecimation(_pixelDecimation); + + // internal + connect(client, &FlatBufferClient::clientDisconnected, this, &FlatBufferServer::clientDisconnected); + connect(client, &FlatBufferClient::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput); + connect(client, &FlatBufferClient::clearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput); + connect(client, &FlatBufferClient::setGlobalInputImage, GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage); + connect(client, &FlatBufferClient::setGlobalInputColor, GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor); + connect(client, &FlatBufferClient::setBufferImage, GlobalSignals::getInstance(), &GlobalSignals::setBufferImage); + _openConnections.append(client); } } } diff --git a/libsrc/forwarder/MessageForwarder.cpp b/libsrc/forwarder/MessageForwarder.cpp index d5a39f870..61d8191d5 100644 --- a/libsrc/forwarder/MessageForwarder.cpp +++ b/libsrc/forwarder/MessageForwarder.cpp @@ -45,8 +45,8 @@ MessageForwarder::MessageForwarder(const QJsonDocument& config) , _priority(DEFAULT_FORWARDER_FLATBUFFFER_PRIORITY) , _isEnabled(false) , _toBeForwardedInstanceID(NO_INSTANCE_ID) - , _hyperion(nullptr) - , _muxer(nullptr) + , _hyperionWeak(nullptr) + , _muxerWeak(nullptr) , _messageForwarderFlatBufHelper(nullptr) { qRegisterMetaType("TargetHost"); @@ -54,6 +54,7 @@ MessageForwarder::MessageForwarder(const QJsonDocument& config) MessageForwarder::~MessageForwarder() { + qDebug() << "MessageForwarder::~MessageForwarder()..."; } void MessageForwarder::init() @@ -76,7 +77,8 @@ void MessageForwarder::start() void MessageForwarder::stop() { - if (!_hyperion.isNull()) + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (!hyperion.isNull()) { disconnect(_toBeForwardedInstanceID); @@ -94,19 +96,23 @@ bool MessageForwarder::connect(quint8 instanceID) { Info(_log, "Connect forwarder to instance [%u]", _toBeForwardedInstanceID); - _hyperion = HyperionIManager::getInstance()->getHyperionInstance(_toBeForwardedInstanceID); - _muxer = _hyperion->getMuxerInstance(); + QSharedPointer const hyperion = HyperionIManager::getInstance()->getHyperionInstance(_toBeForwardedInstanceID); + if (hyperion) + { + _hyperionWeak = hyperion; + _muxerWeak = hyperion->getMuxerInstance(); - // component changes - QObject::connect(_hyperion.get(), &Hyperion::compStateChangeRequest, this, &MessageForwarder::handleCompStateChangeRequest); + // component changes + QObject::connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &MessageForwarder::handleCompStateChangeRequest); - // connect with Muxer visible priority changes - QObject::connect(_muxer.get(), &PriorityMuxer::visiblePriorityChanged, this, &MessageForwarder::handlePriorityChanges); + // connect with Muxer visible priority changes + QObject::connect(_muxerWeak.toStrongRef().get(), &PriorityMuxer::visiblePriorityChanged, this, &MessageForwarder::handlePriorityChanges); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::connect(GlobalSignals::getInstance(), &GlobalSignals::setBufferImage, _hyperion.get(), &Hyperion::forwardBufferMessage); + QObject::connect(GlobalSignals::getInstance(), &GlobalSignals::setBufferImage, hyperion.get(), &Hyperion::forwardBufferMessage); #endif - isConnected = true; + isConnected = true; + } } else { @@ -118,18 +124,16 @@ bool MessageForwarder::connect(quint8 instanceID) void MessageForwarder::disconnect(quint8 instanceID) { - if (instanceID == _toBeForwardedInstanceID && !_hyperion.isNull()) + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (instanceID == _toBeForwardedInstanceID && !hyperion.isNull()) { Debug(_log, "Disconnect forwarder from instance [%u]", instanceID); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::disconnect(GlobalSignals::getInstance(), &GlobalSignals::setBufferImage, _hyperion.get(), &Hyperion::forwardBufferMessage); + QObject::disconnect(GlobalSignals::getInstance(), &GlobalSignals::setBufferImage, hyperion.get(), &Hyperion::forwardBufferMessage); #endif handleTargets(false, _config.object()); - - _hyperion.clear(); - _muxer.clear(); } } @@ -165,7 +169,7 @@ void MessageForwarder::handleCompStateChangeRequest(hyperion::Components compone if (enable) { - if (_hyperion.isNull()) + if (_hyperionWeak.isNull()) { connect(_toBeForwardedInstanceID); } @@ -180,31 +184,43 @@ void MessageForwarder::handleCompStateChangeRequest(hyperion::Components compone bool MessageForwarder::isFlatbufferComponent(int priority) { + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion.isNull()) + { + return false; + } + bool isFlatbufferComponent{ false }; - hyperion::Components const activeCompId = _hyperion->getPriorityInfo(priority).componentId; + hyperion::Components const activeCompId = hyperion->getPriorityInfo(priority).componentId; - switch (activeCompId) { - case hyperion::COMP_GRABBER: - case hyperion::COMP_V4L: - case hyperion::COMP_AUDIO: + switch (activeCompId) { + case hyperion::COMP_GRABBER: + case hyperion::COMP_V4L: + case hyperion::COMP_AUDIO: #if defined(ENABLE_FLATBUF_SERVER) - case hyperion::COMP_FLATBUFSERVER: + case hyperion::COMP_FLATBUFSERVER: #endif #if defined(ENABLE_PROTOBUF_SERVER) - case hyperion::COMP_PROTOSERVER: + case hyperion::COMP_PROTOSERVER: #endif #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - isFlatbufferComponent = true; - break; + isFlatbufferComponent = true; + break; #endif - default: - break; - } + default: + break; + } return isFlatbufferComponent; } bool MessageForwarder::activateFlatbufferTargets(int priority) { + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion.isNull()) + { + return false; + } + int startedFlatbufTargets{ 0 }; if (priority != PriorityMuxer::LOWEST_PRIORITY) @@ -214,16 +230,16 @@ bool MessageForwarder::activateFlatbufferTargets(int priority) startedFlatbufTargets = startFlatbufferTargets(_config.object()); if (startedFlatbufTargets > 0) { - hyperion::Components const activeCompId = _hyperion->getPriorityInfo(priority).componentId; + hyperion::Components const activeCompId = hyperion->getPriorityInfo(priority).componentId; switch (activeCompId) { case hyperion::COMP_GRABBER: - QObject::connect(_hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); + QObject::connect(hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); break; case hyperion::COMP_V4L: - QObject::connect(_hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); + QObject::connect(hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); break; case hyperion::COMP_AUDIO: - QObject::connect(_hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); + QObject::connect(hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); break; #if defined(ENABLE_FLATBUF_SERVER) case hyperion::COMP_FLATBUFSERVER: @@ -233,7 +249,7 @@ bool MessageForwarder::activateFlatbufferTargets(int priority) #endif #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::connect(_hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); + QObject::connect(hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); break; #endif default: @@ -252,13 +268,14 @@ void MessageForwarder::handleTargets(bool start, const QJsonObject& config) stopJsonTargets(); stopFlatbufferTargets(); + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); if (start) { - int const jsonTargetNum = startJsonTargets(config); - - if (!_hyperion.isNull()) + if (!hyperion.isNull()) { - int const currentPriority = _hyperion->getCurrentPriority(); + int const jsonTargetNum = startJsonTargets(config); + + int const currentPriority = hyperion->getCurrentPriority(); bool const isActiveFlatbufferTarget = activateFlatbufferTargets(currentPriority); if (jsonTargetNum > 0 || isActiveFlatbufferTarget) @@ -273,41 +290,42 @@ void MessageForwarder::handleTargets(bool start, const QJsonObject& config) } } - if (!_hyperion.isNull()) + if (!hyperion.isNull()) { - _hyperion->setNewComponentState(hyperion::COMP_FORWARDER, _isActive); + hyperion->setNewComponentState(hyperion::COMP_FORWARDER, _isActive); } } void MessageForwarder::disconnectFlatBufferComponents(int priority) { - if (_hyperion.isNull()) + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion.isNull()) { return; } - hyperion::Components const activeCompId = _hyperion->getPriorityInfo(priority).componentId; + hyperion::Components const activeCompId = hyperion->getPriorityInfo(priority).componentId; switch (activeCompId) { case hyperion::COMP_GRABBER: - QObject::disconnect(_hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::disconnect(_hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); #endif break; case hyperion::COMP_V4L: - QObject::disconnect(_hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::disconnect(_hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); #endif break; case hyperion::COMP_AUDIO: - QObject::disconnect(_hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::disconnect(_hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); #endif break; #if defined(ENABLE_FLATBUF_SERVER) @@ -317,17 +335,17 @@ void MessageForwarder::disconnectFlatBufferComponents(int priority) case hyperion::COMP_PROTOSERVER: #endif #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::disconnect(_hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); break; #endif default: - QObject::disconnect(_hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::disconnect(_hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); #endif break; } @@ -365,7 +383,7 @@ void MessageForwarder::addJsonTarget(const QJsonObject& targetConfig) if (!hostName.isEmpty()) { - if (_hyperion.isNull()) + if (_hyperionWeak.isNull()) { return; } @@ -470,7 +488,7 @@ void MessageForwarder::addFlatbufferTarget(const QJsonObject& targetConfig) if (!hostName.isEmpty()) { - if (_hyperion.isNull()) + if (_hyperionWeak.isNull()) { return; } @@ -557,13 +575,14 @@ void MessageForwarder::stopFlatbufferTargets() { if (!_flatbufferTargets.isEmpty()) { - if (!_hyperion.isNull()) + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (!hyperion.isNull()) { - QObject::disconnect(_hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); - QObject::disconnect(_hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardSystemProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage); #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) - QObject::disconnect(_hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); + QObject::disconnect(hyperion.get(), &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage); #endif } diff --git a/libsrc/grabber/qt/QtGrabber.cpp b/libsrc/grabber/qt/QtGrabber.cpp index 4d2066204..7d31be998 100644 --- a/libsrc/grabber/qt/QtGrabber.cpp +++ b/libsrc/grabber/qt/QtGrabber.cpp @@ -16,22 +16,22 @@ // Constants namespace { -const bool verbose = false; + const bool verbose = false; } //End of constants QtGrabber::QtGrabber(int display, int cropLeft, int cropRight, int cropTop, int cropBottom) : Grabber("GRABBER-QT", cropLeft, cropRight, cropTop, cropBottom) - , _display(display) - , _numberOfSDisplays(0) - , _screenWidth(0) - , _screenHeight(0) - , _src_x(0) - , _src_y(0) - , _src_x_max(0) - , _src_y_max(0) - , _isWayland(false) - , _screen(nullptr) - , _isVirtual(false) + , _display(display) + , _numberOfSDisplays(0) + , _screenWidth(0) + , _screenHeight(0) + , _src_x(0) + , _src_y(0) + , _src_x_max(0) + , _src_y_max(0) + , _isWayland(false) + , _screen(nullptr) + , _isVirtual(false) { _useImageResampler = false; } @@ -48,13 +48,13 @@ void QtGrabber::freeResources() bool QtGrabber::isAvailable(bool logError) { - #ifndef _WIN32 +#ifndef _WIN32 if (getenv("WAYLAND_DISPLAY") != nullptr) { ErrorIf(logError, _log, "Grabber does not work under Wayland!"); _isWayland = true; } - #endif +#endif _isAvailable = !_isWayland; return _isAvailable; @@ -106,7 +106,8 @@ bool QtGrabber::setupDisplay() for (auto* screen : std::as_const(screens)) { const QRect geo = screen->geometry(); - Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.width(), geo.height(), geo.x(), geo.y(), geo.x() + geo.width(), geo.y() + geo.height(), screen->depth()); + qreal devicePixelRatio = screen->devicePixelRatio(); + Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit DPR:%.2f", index, QSTRING_CSTR(screen->name()), geo.width(), geo.height(), geo.x(), geo.y(), geo.x() + geo.width(), geo.y() + geo.height(), screen->depth(), devicePixelRatio); ++index; } @@ -114,7 +115,8 @@ bool QtGrabber::setupDisplay() if (screens.at(0)->size() != screens.at(0)->virtualSize()) { const QRect vgeo = screens.at(0)->virtualGeometry(); - Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", _numberOfSDisplays, "All Displays", vgeo.width(), vgeo.height(), vgeo.x(), vgeo.y(), vgeo.x() + vgeo.width(), vgeo.y() + vgeo.height(), screens.at(0)->depth()); + qreal devicePixelRatio = screens.at(0)->devicePixelRatio(); + Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit DPR:%.2f", _numberOfSDisplays, "All Displays", vgeo.width(), vgeo.height(), vgeo.x(), vgeo.y(), vgeo.x() + vgeo.width(), vgeo.y() + vgeo.height(), screens.at(0)->depth(), devicePixelRatio); } _isVirtual = false; @@ -138,6 +140,7 @@ bool QtGrabber::setupDisplay() // init the requested display _screen = screens.at(_display); connect(_screen, &QScreen::geometryChanged, this, &QtGrabber::geometryChanged); + connect(_screen, &QScreen::physicalDotsPerInchChanged, this, &QtGrabber::pixelRatioChanged); updateScreenDimensions(true); if (_isVirtual) @@ -160,50 +163,75 @@ void QtGrabber::geometryChanged(const QRect& geo) updateScreenDimensions(true); } +void QtGrabber::pixelRatioChanged(qreal /* dpi */) +{ + Info(_log, "The pixel ratio changed to %.2f", _screen->devicePixelRatio()); +} + + #ifdef _WIN32 extern QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int format = 0); QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int height) const { - QSize windowSize; HWND hwnd = reinterpret_cast(window); - if (hwnd) - { - RECT r; - GetClientRect(hwnd, &r); - windowSize = QSize(r.right - r.left, r.bottom - r.top); - } - else + + QSize nativeSize(width, height); + if (!nativeSize.isValid()) { - hwnd = GetDesktopWindow(); - const QRect screenGeometry = _screen->geometry(); - windowSize = screenGeometry.size(); + QSize windowSize; + if (hwnd) + { + RECT r; + GetClientRect(hwnd, &r); + windowSize = QSize(r.right - r.left, r.bottom - r.top); + } + else + { + hwnd = GetDesktopWindow(); + const QRect screenGeometry = _screen->geometry(); + windowSize = screenGeometry.size(); + } + + if (width < 0) + { + nativeSize.setWidth(windowSize.width() - xIn); + } + + if (height < 0) + { + nativeSize.setHeight(windowSize.height() - yIn); + } } - if (width < 0) - width = windowSize.width() - xIn; + // Get device pixel ratio to handle high-DPI displays + qreal devicePixelRatio = _screen->devicePixelRatio(); - if (height < 0) - height = windowSize.height() - yIn; + // Scale coordinates by device pixel ratio for physical pixel accuracy + QPoint const nativePos = QPoint(xIn, yIn) * devicePixelRatio; + nativeSize *= devicePixelRatio; // Create and setup bitmap HDC display_dc = GetDC(nullptr); HDC bitmap_dc = CreateCompatibleDC(display_dc); - HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height); + HBITMAP bitmap = CreateCompatibleBitmap(display_dc, nativeSize.width(), nativeSize.height()); HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap); // copy data HDC window_dc = GetDC(hwnd); - BitBlt(bitmap_dc, 0, 0, width, height, window_dc, xIn, yIn, SRCCOPY); + BitBlt(bitmap_dc, 0, 0, nativeSize.width(), nativeSize.height(), window_dc, nativePos.x(), nativePos.y(), SRCCOPY); // clean up all but bitmap ReleaseDC(hwnd, window_dc); SelectObject(bitmap_dc, null_bitmap); DeleteDC(bitmap_dc); - const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap); + QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap); DeleteObject(bitmap); ReleaseDC(nullptr, display_dc); + // Set device pixel ratio on the pixmap to ensure proper scaling + pixmap.setDevicePixelRatio(devicePixelRatio); + return pixmap; } #endif @@ -269,7 +297,10 @@ int QtGrabber::updateScreenDimensions(bool force) return 0; } - Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, geo.width(), geo.height()); + // Get device pixel ratio for high-DPI display handling + qreal devicePixelRatio = _screen->devicePixelRatio(); + + Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d] (Device Pixel Ratio: %.2f)", _screenWidth, _screenHeight, geo.width(), geo.height(), devicePixelRatio); _screenWidth = geo.width(); _screenHeight = geo.height(); @@ -278,12 +309,12 @@ int QtGrabber::updateScreenDimensions(bool force) // Image scaling is performed by Qt width = (_screenWidth > (_cropLeft + _cropRight)) - ? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation) - : (_screenWidth / _pixelDecimation); + ? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation) + : (_screenWidth / _pixelDecimation); height = (_screenHeight > (_cropTop + _cropBottom)) - ? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation) - : (_screenHeight / _pixelDecimation); + ? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation) + : (_screenHeight / _pixelDecimation); // calculate final image dimensions and adjust top/left cropping in 3D modes if (_isVirtual) @@ -327,7 +358,13 @@ int QtGrabber::updateScreenDimensions(bool force) } Info(_log, "Update output image resolution to [%dx%d]", _width, _height); - Debug(_log, "Grab screen area: %d,%d,%d,%d", _src_x, _src_y, _src_x_max, _src_y_max); + + // Scale coordinates by device pixel ratio to get native resolution + QPoint const nativePos = QPoint(_src_x, _src_y) * devicePixelRatio; + QSize const nativeSize = QSize(_src_x_max, _src_y_max) * devicePixelRatio; + + Debug(_log, "Grab screen area: %d,%d,%d,%d (Physical: %d,%d,%d,%d)", _src_x, _src_y, _src_x_max, _src_y_max, + nativePos.x(), nativePos.y(), nativeSize.width(), nativeSize.height()); return 1; } diff --git a/libsrc/hyperion/BGEffectHandler.cpp b/libsrc/hyperion/BGEffectHandler.cpp index 4b28eebe8..6d57570fd 100644 --- a/libsrc/hyperion/BGEffectHandler.cpp +++ b/libsrc/hyperion/BGEffectHandler.cpp @@ -5,25 +5,42 @@ #include -BGEffectHandler::BGEffectHandler(Hyperion* hyperion) - : QObject(hyperion) - , _hyperion(hyperion) +BGEffectHandler::BGEffectHandler(const QSharedPointer& hyperionInstance) + : QObject() + , _hyperionWeak(hyperionInstance) , _isBgEffectEnabled(false) { - QString subComponent = parent()->property("instance").toString(); + QString subComponent{ "__" }; + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + } _log = Logger::getInstance("HYPERION", subComponent); - QObject::connect(_hyperion, &Hyperion::settingsChanged, this, &BGEffectHandler::handleSettingsUpdate); - QObject::connect(_hyperion->getMuxerInstance().get(), &PriorityMuxer::prioritiesChanged, this, &BGEffectHandler::handlePriorityUpdate); + if (hyperion) + { + QObject::connect(hyperion.get(), &Hyperion::settingsChanged, this, &BGEffectHandler::handleSettingsUpdate); + QObject::connect(hyperion->getMuxerInstance().get(), &PriorityMuxer::prioritiesChanged, this, &BGEffectHandler::handlePriorityUpdate); + } // initialization - handleSettingsUpdate(settings::BGEFFECT, _hyperion->getSetting(settings::BGEFFECT)); + handleSettingsUpdate(settings::BGEFFECT, hyperion->getSetting(settings::BGEFFECT)); +} + +BGEffectHandler::~BGEffectHandler() +{ + qDebug() << "BGEffectHandler::~BGEffectHandler()..."; } void BGEffectHandler::disconnect() { - QObject::disconnect(_hyperion->getMuxerInstance().get(), &PriorityMuxer::prioritiesChanged, this, &BGEffectHandler::handlePriorityUpdate); - QObject::disconnect(_hyperion, &Hyperion::settingsChanged, this, &BGEffectHandler::handleSettingsUpdate); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + QObject::disconnect(hyperion->getMuxerInstance().get(), &PriorityMuxer::prioritiesChanged, this, &BGEffectHandler::handlePriorityUpdate); + QObject::disconnect(hyperion.get(), &Hyperion::settingsChanged, this, &BGEffectHandler::handleSettingsUpdate); + } } bool BGEffectHandler::_isEnabled () const @@ -37,12 +54,14 @@ void BGEffectHandler::handleSettingsUpdate(settings::type type, const QJsonDocum { _bgEffectConfig = config; + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); const QJsonObject& BGEffectConfig = _bgEffectConfig.object(); + #define BGCONFIG_ARRAY bgColorConfig.toArray() // clear background priority - if (_hyperion->getCurrentPriority() == PriorityMuxer::BG_PRIORITY) + if (hyperion->getCurrentPriority() == PriorityMuxer::BG_PRIORITY) { - _hyperion->clear(PriorityMuxer::BG_PRIORITY); + hyperion->clear(PriorityMuxer::BG_PRIORITY); } _isBgEffectEnabled = BGEffectConfig["enable"].toBool(true); @@ -65,13 +84,13 @@ void BGEffectHandler::handleSettingsUpdate(settings::type type, const QJsonDocum static_cast(BGCONFIG_ARRAY.at(2).toInt(0)) } }; - _hyperion->setColor(PriorityMuxer::BG_PRIORITY, bg_color); + hyperion->setColor(PriorityMuxer::BG_PRIORITY, bg_color); Info(_log,"Initial background color set (%d %d %d)",bg_color.at(0).red, bg_color.at(0).green, bg_color.at(0).blue); } #if defined(ENABLE_EFFECTENGINE) else { - int result = _hyperion->setEffect(bgEffectConfig, PriorityMuxer::BG_PRIORITY, PriorityMuxer::ENDLESS); + int result = hyperion->setEffect(bgEffectConfig, PriorityMuxer::BG_PRIORITY, PriorityMuxer::ENDLESS); Info(_log,"Initial background effect '%s' %s", QSTRING_CSTR(bgEffectConfig), ((result == 0) ? "started" : "failed")); } #endif @@ -82,13 +101,14 @@ void BGEffectHandler::handleSettingsUpdate(settings::type type, const QJsonDocum void BGEffectHandler::handlePriorityUpdate(int currentPriority) { - if (currentPriority < PriorityMuxer::BG_PRIORITY && _hyperion->getMuxerInstance()->hasPriority(PriorityMuxer::BG_PRIORITY)) + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (currentPriority < PriorityMuxer::BG_PRIORITY && hyperion->getMuxerInstance()->hasPriority(PriorityMuxer::BG_PRIORITY)) { Debug(_log,"Stop background (color-) effect as it moved out of scope"); - _hyperion->clear(PriorityMuxer::BG_PRIORITY); + hyperion->clear(PriorityMuxer::BG_PRIORITY); } // Do not start a background effect when the overall instance is disabled - else if (_hyperion->isComponentEnabled(hyperion::COMP_ALL)) + else if (hyperion->isComponentEnabled(hyperion::COMP_ALL)) { if (currentPriority == PriorityMuxer::LOWEST_PRIORITY && _isBgEffectEnabled) { diff --git a/libsrc/hyperion/CaptureCont.cpp b/libsrc/hyperion/CaptureCont.cpp index 9dfb5accf..5f0b3d44d 100644 --- a/libsrc/hyperion/CaptureCont.cpp +++ b/libsrc/hyperion/CaptureCont.cpp @@ -21,98 +21,139 @@ constexpr std::chrono::seconds DEFAULT_SCREEN_CAPTURE_INACTIVE_TIMEOUT{5}; constexpr std::chrono::seconds DEFAULT_AUDIO_CAPTURE_INACTIVE_TIMEOUT{1}; } -CaptureCont::CaptureCont(Hyperion* hyperion) - : _hyperion(hyperion) +CaptureCont::CaptureCont(const QSharedPointer& hyperionInstance) + : _hyperionWeak(hyperionInstance) , _screenCaptureEnabled(false) , _screenCapturePriority(0) - , _screenCaptureInactiveTimer(new QTimer(this)) + , _screenCaptureInactiveTimer(nullptr) , _videoCaptureEnabled(false) , _videoCapturePriority(0) - , _videoInactiveTimer(new QTimer(this)) + , _videoInactiveTimer(nullptr) , _audioCaptureEnabled(false) , _audioCapturePriority(0) - , _audioCaptureInactiveTimer(new QTimer(this)) + , _audioCaptureInactiveTimer(nullptr) { - // settings changes - connect(_hyperion, &Hyperion::settingsChanged, this, &CaptureCont::handleSettingsUpdate); +} + +CaptureCont::~CaptureCont() +{ + qDebug() << "CaptureCont::~CaptureCont()..."; +} + +void CaptureCont::start() +{ + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + // settings changes + connect(hyperion.get(), &Hyperion::settingsChanged, this, &CaptureCont::handleSettingsUpdate); + + // comp changes + connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &CaptureCont::handleCompStateChangeRequest); - // comp changes - connect(_hyperion, &Hyperion::compStateChangeRequest, this, &CaptureCont::handleCompStateChangeRequest); - // inactive timer screen - connect(_screenCaptureInactiveTimer, &QTimer::timeout, this, &CaptureCont::onScreenIsInactive); - _screenCaptureInactiveTimer->setSingleShot(true); - _screenCaptureInactiveTimer->setInterval(DEFAULT_SCREEN_CAPTURE_INACTIVE_TIMEOUT); + // inactive timer screen + _screenCaptureInactiveTimer.reset(new QTimer(this)); + connect(_screenCaptureInactiveTimer.get(), &QTimer::timeout, this, &CaptureCont::onScreenIsInactive); + _screenCaptureInactiveTimer->setSingleShot(true); + _screenCaptureInactiveTimer->setInterval(DEFAULT_SCREEN_CAPTURE_INACTIVE_TIMEOUT); - // inactive timer video - connect(_videoInactiveTimer, &QTimer::timeout, this, &CaptureCont::onVideoIsInactive); - _videoInactiveTimer->setSingleShot(true); - _videoInactiveTimer->setInterval(DEFAULT_VIDEO_CAPTURE_INACTIVE_TIMEOUT); + // inactive timer video + _videoInactiveTimer.reset(new QTimer(this)); + connect(_videoInactiveTimer.get(), &QTimer::timeout, this, &CaptureCont::onVideoIsInactive); + _videoInactiveTimer->setSingleShot(true); + _videoInactiveTimer->setInterval(DEFAULT_VIDEO_CAPTURE_INACTIVE_TIMEOUT); - // inactive timer audio - connect(_audioCaptureInactiveTimer, &QTimer::timeout, this, &CaptureCont::onAudioIsInactive); - _audioCaptureInactiveTimer->setSingleShot(true); - _audioCaptureInactiveTimer->setInterval(DEFAULT_AUDIO_CAPTURE_INACTIVE_TIMEOUT); + // inactive timer audio + _audioCaptureInactiveTimer.reset(new QTimer(this)); + connect(_audioCaptureInactiveTimer.get(), &QTimer::timeout, this, &CaptureCont::onAudioIsInactive); + _audioCaptureInactiveTimer->setSingleShot(true); + _audioCaptureInactiveTimer->setInterval(DEFAULT_AUDIO_CAPTURE_INACTIVE_TIMEOUT); - // init - handleSettingsUpdate(settings::INSTCAPTURE, _hyperion->getSetting(settings::INSTCAPTURE)); + // init + handleSettingsUpdate(settings::INSTCAPTURE, hyperion->getSetting(settings::INSTCAPTURE)); + } +} + +void CaptureCont::stop() +{ + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + disconnect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &CaptureCont::handleCompStateChangeRequest); + disconnect(hyperion.get(), &Hyperion::settingsChanged, this, &CaptureCont::handleSettingsUpdate); + } + + _videoInactiveTimer->stop(); + _screenCaptureInactiveTimer->stop(); + _audioCaptureInactiveTimer->stop(); + + disconnect(_videoInactiveTimer.get(), &QTimer::timeout, this, &CaptureCont::onVideoIsInactive); + disconnect(_screenCaptureInactiveTimer.get(), &QTimer::timeout, this, &CaptureCont::onScreenIsInactive); + disconnect(_audioCaptureInactiveTimer.get(), &QTimer::timeout, this, &CaptureCont::onAudioIsInactive); } void CaptureCont::handleVideoImage(const QString& name, const Image & image) { + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); if(_videoCaptureName != name) { - _hyperion->registerInput(_videoCapturePriority, hyperion::COMP_V4L, "System", name); + hyperion->registerInput(_videoCapturePriority, hyperion::COMP_V4L, "System", name); _videoCaptureName = name; - emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_V4L, int(_hyperion->getInstanceIndex()), _videoCaptureEnabled); + emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_V4L, int(hyperion->getInstanceIndex()), _videoCaptureEnabled); } _videoInactiveTimer->start(); - _hyperion->setInputImage(_videoCapturePriority, image); + hyperion->setInputImage(_videoCapturePriority, image); } void CaptureCont::handleScreenImage(const QString& name, const Image& image) { + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); if(_screenCaptureName != name) { - _hyperion->registerInput(_screenCapturePriority, hyperion::COMP_GRABBER, "System", name); + hyperion->registerInput(_screenCapturePriority, hyperion::COMP_GRABBER, "System", name); _screenCaptureName = name; - emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_GRABBER, int(_hyperion->getInstanceIndex()), _screenCaptureEnabled); + emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_GRABBER, int(hyperion->getInstanceIndex()), _screenCaptureEnabled); } _screenCaptureInactiveTimer->start(); - _hyperion->setInputImage(_screenCapturePriority, image); + hyperion->setInputImage(_screenCapturePriority, image); } void CaptureCont::handleAudioImage(const QString& name, const Image& image) { + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); if (_audioCaptureName != name) { - _hyperion->registerInput(_audioCapturePriority, hyperion::COMP_AUDIO, "System", name); + hyperion->registerInput(_audioCapturePriority, hyperion::COMP_AUDIO, "System", name); _audioCaptureName = name; } _audioCaptureInactiveTimer->start(); - _hyperion->setInputImage(_audioCapturePriority, image); + hyperion->setInputImage(_audioCapturePriority, image); } void CaptureCont::setScreenCaptureEnable(bool enable) { if(_screenCaptureEnabled != enable) { + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); if(enable) { - _hyperion->registerInput(_screenCapturePriority, hyperion::COMP_GRABBER); + hyperion->registerInput(_screenCapturePriority, hyperion::COMP_GRABBER); connect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, &CaptureCont::handleScreenImage); - connect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, _hyperion, &Hyperion::forwardSystemProtoMessage); + connect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, hyperion.get(), &Hyperion::forwardSystemProtoMessage); } else { + disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, hyperion.get(), &Hyperion::forwardSystemProtoMessage); disconnect(GlobalSignals::getInstance(), &GlobalSignals::setSystemImage, this, nullptr); - _hyperion->clear(_screenCapturePriority); + + hyperion->clear(_screenCapturePriority); _screenCaptureInactiveTimer->stop(); _screenCaptureName = ""; } _screenCaptureEnabled = enable; - _hyperion->setNewComponentState(hyperion::COMP_GRABBER, enable); - emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_GRABBER, int(_hyperion->getInstanceIndex()), enable); + hyperion->setNewComponentState(hyperion::COMP_GRABBER, enable); + emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_GRABBER, int(hyperion->getInstanceIndex()), enable); } } @@ -120,22 +161,25 @@ void CaptureCont::setVideoCaptureEnable(bool enable) { if(_videoCaptureEnabled != enable) { + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); if(enable) { - _hyperion->registerInput(_videoCapturePriority, hyperion::COMP_V4L); + hyperion->registerInput(_videoCapturePriority, hyperion::COMP_V4L); connect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, this, &CaptureCont::handleVideoImage); - connect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, _hyperion, &Hyperion::forwardV4lProtoMessage); + connect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, hyperion.get(), &Hyperion::forwardV4lProtoMessage); } else { + disconnect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, hyperion.get(), &Hyperion::forwardV4lProtoMessage); disconnect(GlobalSignals::getInstance(), &GlobalSignals::setV4lImage, this, nullptr); - _hyperion->clear(_videoCapturePriority); + + hyperion->clear(_videoCapturePriority); _videoInactiveTimer->stop(); _videoCaptureName = ""; } _videoCaptureEnabled = enable; - _hyperion->setNewComponentState(hyperion::COMP_V4L, enable); - emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_V4L, int(_hyperion->getInstanceIndex()), enable); + hyperion->setNewComponentState(hyperion::COMP_V4L, enable); + emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_V4L, int(hyperion->getInstanceIndex()), enable); } } @@ -143,22 +187,25 @@ void CaptureCont::setAudioCaptureEnable(bool enable) { if (_audioCaptureEnabled != enable) { + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); if (enable) { - _hyperion->registerInput(_audioCapturePriority, hyperion::COMP_AUDIO); + hyperion->registerInput(_audioCapturePriority, hyperion::COMP_AUDIO); connect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, this, &CaptureCont::handleAudioImage); - connect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, _hyperion, &Hyperion::forwardAudioProtoMessage); + connect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, hyperion.get(), &Hyperion::forwardAudioProtoMessage); } else { + disconnect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, hyperion.get(), &Hyperion::forwardAudioProtoMessage); disconnect(GlobalSignals::getInstance(), &GlobalSignals::setAudioImage, this, nullptr); - _hyperion->clear(_audioCapturePriority); + + hyperion->clear(_audioCapturePriority); _audioCaptureInactiveTimer->stop(); _audioCaptureName = ""; } _audioCaptureEnabled = enable; - _hyperion->setNewComponentState(hyperion::COMP_AUDIO, enable); - emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_AUDIO, int(_hyperion->getInstanceIndex()), enable); + hyperion->setNewComponentState(hyperion::COMP_AUDIO, enable); + emit GlobalSignals::getInstance()->requestSource(hyperion::COMP_AUDIO, int(hyperion->getInstanceIndex()), enable); } } @@ -231,15 +278,15 @@ void CaptureCont::handleCompStateChangeRequest(hyperion::Components component, b void CaptureCont::onVideoIsInactive() { - _hyperion->setInputInactive(_videoCapturePriority); + _hyperionWeak.toStrongRef()->setInputInactive(_videoCapturePriority); } void CaptureCont::onScreenIsInactive() { - _hyperion->setInputInactive(_screenCapturePriority); + _hyperionWeak.toStrongRef()->setInputInactive(_screenCapturePriority); } void CaptureCont::onAudioIsInactive() { - _hyperion->setInputInactive(_audioCapturePriority); + _hyperionWeak.toStrongRef()->setInputInactive(_audioCapturePriority); } diff --git a/libsrc/hyperion/ComponentRegister.cpp b/libsrc/hyperion/ComponentRegister.cpp index f6b72cb1c..a52c615c9 100644 --- a/libsrc/hyperion/ComponentRegister.cpp +++ b/libsrc/hyperion/ComponentRegister.cpp @@ -1,5 +1,4 @@ #include -#include #include @@ -7,25 +6,28 @@ using namespace hyperion; -ComponentRegister::ComponentRegister(Hyperion* hyperion) - : _hyperion(hyperion) +ComponentRegister::ComponentRegister(const QSharedPointer& hyperionInstance) + : QObject() + , _hyperionWeak(hyperionInstance) , _log(nullptr) { - QString subComponent {"__"}; - if (_hyperion != nullptr) + QString subComponent{ "__" }; + + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) { subComponent = hyperion->property("instance").toString(); } - _log= Logger::getInstance("COMPONENTREG", subComponent); + _log = Logger::getInstance("COMPONENTREG", subComponent); // init all comps to false QVector vect; vect << COMP_ALL << COMP_SMOOTHING << COMP_LEDDEVICE; - bool areScreenGrabberAvailable = !GrabberWrapper::availableGrabbers(GrabberTypeFilter::VIDEO).isEmpty(); - bool areVideoGrabberAvailable = !GrabberWrapper::availableGrabbers(GrabberTypeFilter::VIDEO).isEmpty(); - bool areAudioGrabberAvailable = !GrabberWrapper::availableGrabbers(GrabberTypeFilter::AUDIO).isEmpty(); - bool flatBufServerAvailable { false }; + bool const areScreenGrabberAvailable = !GrabberWrapper::availableGrabbers(GrabberTypeFilter::SCREEN).isEmpty(); + bool const areVideoGrabberAvailable = !GrabberWrapper::availableGrabbers(GrabberTypeFilter::VIDEO).isEmpty(); + bool const areAudioGrabberAvailable = !GrabberWrapper::availableGrabbers(GrabberTypeFilter::AUDIO).isEmpty(); + bool flatBufServerAvailable{ false }; bool protoBufServerAvailable{ false }; #if defined(ENABLE_FLATBUF_SERVER) @@ -65,30 +67,31 @@ ComponentRegister::ComponentRegister(Hyperion* hyperion) vect << COMP_FORWARDER; #endif - for(auto e : std::as_const(vect)) + for (auto e : std::as_const(vect)) { _componentStates.emplace(e, (e == COMP_ALL)); } - if (hyperion != nullptr) + if (hyperion) { - connect(_hyperion, &Hyperion::compStateChangeRequest, this, &ComponentRegister::handleCompStateChangeRequest); - connect(_hyperion, &Hyperion::compStateChangeRequestAll, this, &ComponentRegister::handleCompStateChangeRequestAll); + connect(hyperion.get(), &Hyperion::compStateChangeRequest, this, &ComponentRegister::handleCompStateChangeRequest); + connect(hyperion.get(), &Hyperion::compStateChangeRequestAll, this, &ComponentRegister::handleCompStateChangeRequestAll); } } ComponentRegister::~ComponentRegister() { + qDebug() << "ComponentRegister::~ComponentRegister()..."; } int ComponentRegister::isComponentEnabled(hyperion::Components comp) const { - return (_componentStates.count(comp)) ? _componentStates.at(comp) : -1; + auto iter = _componentStates.find(comp); + return (iter != _componentStates.end()) ? int(iter->second) : -1; } void ComponentRegister::setNewComponentState(hyperion::Components comp, bool isActive) { - if (_componentStates.count(comp) > 0) { if (_componentStates[comp] != isActive) @@ -125,7 +128,7 @@ void ComponentRegister::handleCompStateChangeRequestAll(bool isActive, const Com Debug(_log,"Disable selected Hyperion components, store their current state"); } - for(const auto &comp : _componentStates) + for (const auto& comp : _componentStates) { if (!excludeList.contains(comp.first) && comp.first != COMP_ALL) { @@ -134,7 +137,11 @@ void ComponentRegister::handleCompStateChangeRequestAll(bool isActive, const Com // disable if enabled if(comp.second) { - emit _hyperion->compStateChangeRequest(comp.first, false); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + emit hyperion->compStateChangeRequest(comp.first, false); + } } } } @@ -164,7 +171,11 @@ void ComponentRegister::handleCompStateChangeRequestAll(bool isActive, const Com // if comp was enabled, enable again if(comp.second) { - emit _hyperion->compStateChangeRequest(comp.first, true); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + emit hyperion->compStateChangeRequest(comp.first, true); + } } } } diff --git a/libsrc/hyperion/Grabber.cpp b/libsrc/hyperion/Grabber.cpp index 4637962eb..37ea49e7c 100644 --- a/libsrc/hyperion/Grabber.cpp +++ b/libsrc/hyperion/Grabber.cpp @@ -18,6 +18,7 @@ Grabber::Grabber(const QString& grabberName, int cropLeft, int cropRight, int cr , _cropRight(0) , _cropTop(0) , _cropBottom(0) + , _isCropping(false) , _isAvailable(true) , _isEnabled(true) , _isDeviceInError(false) @@ -90,7 +91,7 @@ void Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBott { if (cropLeft + cropRight >= _width || cropTop + cropBottom >= _height) { - Error(_log, "Rejecting invalid crop values: left: %d, right: %d, top: %d, bottom: %d, higher than height/width %d/%d", cropLeft, cropRight, cropTop, cropBottom, _height, _width); + Error(_log, "Rejecting invalid crop values: left: %d, right: %d, top: %d, bottom: %d, greater than or equal to width/height %d/%d", cropLeft, cropRight, cropTop, cropBottom, _width, _height); return; } } @@ -112,6 +113,11 @@ void Grabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBott if (cropLeft > 0 || cropRight > 0 || cropTop > 0 || cropBottom > 0) { Info(_log, "Cropping image: width=%d height=%d; crop: left=%d right=%d top=%d bottom=%d ", _width, _height, cropLeft, cropRight, cropTop, cropBottom); + _isCropping = true; + } + else + { + _isCropping = false; } } diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index fb8fa57db..cf21eb9bb 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -85,10 +85,16 @@ Hyperion::Hyperion(quint8 instance, QObject* parent) qRegisterMetaType("ComponentList"); qRegisterMetaType>("ColorRgbImage"); - QString const subComponent = "I"+QString::number(_instIndex); + QString const subComponent = "I" + QString::number(_instIndex); this->setProperty("instance", QVariant::fromValue(subComponent)); - _log= Logger::getInstance("HYPERION", subComponent); + _log = Logger::getInstance("HYPERION", subComponent); +} + +Hyperion::~Hyperion() +{ + Debug(_log, "Hyperion instance [%u] is stopping...", _instIndex); + qDebug() << "Hyperion::~Hyperion() - Hyperion instance [" << _instIndex << "] is stopping..."; } void Hyperion::start() @@ -102,7 +108,8 @@ void Hyperion::start() // listen for settings updates of this instance (LEDS & COLOR) connect(_settingsManager.get(), &SettingsManager::settingsChanged, this, &Hyperion::handleSettingsUpdate); - _componentRegister = MAKE_TRACKED_SHARED(ComponentRegister, this); + _componentRegister = MAKE_TRACKED_SHARED(ComponentRegister, sharedFromThis()); + connect(this, &Hyperion::isSetNewComponentState, _componentRegister.get(), &ComponentRegister::setNewComponentState); // get newVideoMode from HyperionIManager connect(this, &Hyperion::newVideoMode, this, &Hyperion::handleNewVideoMode); @@ -123,14 +130,15 @@ void Hyperion::start() _ledBuffer = std::vector(static_cast(_hwLedCount), ColorRgb::BLACK); // smoothing - _deviceSmooth.reset(new LinearColorSmoothing(getSetting(settings::SMOOTHING).object(), this)); + _deviceSmooth = MAKE_TRACKED_SHARED(LinearColorSmoothing, getSetting(settings::SMOOTHING).object(), sharedFromThis()); + connect(this, &Hyperion::settingsChanged, _deviceSmooth.get(), &LinearColorSmoothing::handleSettingsUpdate); _deviceSmooth->start(); // initialize LED-devices QJsonObject const ledDeviceSettings = getSetting(settings::DEVICE).object(); - _ledDeviceWrapper.reset(new LedDeviceWrapper(this)); + _ledDeviceWrapper = MAKE_TRACKED_SHARED(LedDeviceWrapper, sharedFromThis()); connect(this, &Hyperion::compStateChangeRequest, _ledDeviceWrapper.get(), &LedDeviceWrapper::handleComponentState); connect(this, &Hyperion::ledDeviceData, _ledDeviceWrapper.get(), &LedDeviceWrapper::updateLeds); @@ -139,12 +147,12 @@ void Hyperion::start() // listen for suspend/resume, idle requests to perform core activation/deactivation actions connect(this, &Hyperion::suspendRequest, this, &Hyperion::setSuspend); connect(this, &Hyperion::idleRequest, this, &Hyperion::setIdle); - + _muxer->start(); #if defined(ENABLE_EFFECTENGINE) // create the effect engine; needs to be initialized after smoothing! - _effectEngine = MAKE_TRACKED_SHARED(EffectEngine, this); + _effectEngine = MAKE_TRACKED_SHARED(EffectEngine, sharedFromThis()); connect(_effectEngine.get(), &EffectEngine::effectListUpdated, this, &Hyperion::effectListUpdated); connect(this, &Hyperion::stopEffects, _effectEngine.get(), &EffectEngine::stopAllEffects); #endif @@ -152,10 +160,11 @@ void Hyperion::start() hyperion::handleInitialEffect(this, getSetting(settings::FGEFFECT).object()); // handle background effect - _BGEffectHandler.reset(new BGEffectHandler(this)); + _BGEffectHandler = MAKE_TRACKED_SHARED(BGEffectHandler, sharedFromThis()); // create the Daemon capture interface - _captureCont.reset(new CaptureCont(this)); + _captureCont = MAKE_TRACKED_SHARED(CaptureCont, sharedFromThis()); + _captureCont->start(); // link global signals with the corresponding slots connect(GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput, this, &Hyperion::registerInput); @@ -177,7 +186,8 @@ void Hyperion::start() #if defined(ENABLE_BOBLIGHT_SERVER) // boblight, can't live in global scope as it depends on layout - _boblightServer.reset(new BoblightServer(this, getSetting(settings::BOBLSERVER))); + _boblightServer = MAKE_TRACKED_SHARED(BoblightServer, sharedFromThis(), getSetting(settings::BOBLSERVER)); + connect(this, &Hyperion::settingsChanged, _boblightServer.get(), &BoblightServer::handleSettingsUpdate); #endif @@ -192,49 +202,62 @@ void Hyperion::stop(const QString name) //Disconnect Background effect first that it does not kick in when other priorities are stopped _BGEffectHandler->disconnect(); + _captureCont->stop(); + #if defined(ENABLE_BOBLIGHT_SERVER) _boblightServer->stop(); + _boblightServer.clear(); #endif //Remove all priorities _muxer->clearAll(true); #if defined(ENABLE_EFFECTENGINE) - _effectEngine->stopAllEffects(); - #endif + _effectEngine->stopAllEffects(); + _effectEngine.clear(); +#endif + + _muxer->stop(); + _deviceSmooth->stop(); + _ledDeviceWrapper->stopDevice(); - _ledDeviceWrapper->stopDevice(); - _deviceSmooth->stop(); - _muxer->stop(); + //Clear all objects maintained/shared by being the master + _BGEffectHandler.clear(); + _captureCont.clear(); + _deviceSmooth.clear(); + _ledDeviceWrapper.clear(); + _muxer.clear(); + _imageProcessor.clear(); + _componentRegister.clear(); emit finished(name); } void Hyperion::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { - if(type == settings::COLOR) + if (type == settings::COLOR) { updateLedColorAdjustment(_layoutLedCount, config.object()); refreshUpdate(); } - else if(type == settings::LEDS) + else if (type == settings::LEDS) { - #if defined(ENABLE_EFFECTENGINE) +#if defined(ENABLE_EFFECTENGINE) // stop and cache all running effects, as effects depend heavily on LED-layout _effectEngine->cacheRunningEffects(); - #endif +#endif updateLedLayout(config.array()); - #if defined(ENABLE_EFFECTENGINE) +#if defined(ENABLE_EFFECTENGINE) // start cached effects _effectEngine->startCachedEffects(); - #endif +#endif refreshUpdate(); } - else if(type == settings::DEVICE) + else if (type == settings::DEVICE) { QJsonObject const deviceConfig = config.object(); @@ -274,7 +297,7 @@ void Hyperion::updateLedLayout(const QJsonArray& ledLayout) if (_imageProcessor.isNull()) { - _imageProcessor = MAKE_TRACKED_SHARED(ImageProcessor, _ledString, this); + _imageProcessor = MAKE_TRACKED_SHARED(ImageProcessor, _ledString, sharedFromThis()); } else { @@ -285,7 +308,7 @@ void Hyperion::updateLedLayout(const QJsonArray& ledLayout) if (_layoutLedCount < static_cast(_ledBuffer.size())) { - std::fill(_ledBuffer.begin() + _layoutLedCount, _ledBuffer.end(), ColorRgb{0, 0, 0}); + std::fill(_ledBuffer.begin() + _layoutLedCount, _ledBuffer.end(), ColorRgb{ 0, 0, 0 }); } } @@ -298,7 +321,7 @@ QJsonDocument Hyperion::getSetting(settings::type type) const QJsonObject Hyperion::getQJsonConfig(quint8 inst) const { const QJsonObject instanceConfig = _settingsManager->getSettings(inst); - const QJsonObject globalConfig = _settingsManager->getSettings({},QStringList()); + const QJsonObject globalConfig = _settingsManager->getSettings({}, QStringList()); return JsonUtils::mergeJsonObjects(instanceConfig, globalConfig); } @@ -344,7 +367,12 @@ bool Hyperion::sourceAutoSelectEnabled() const void Hyperion::setNewComponentState(hyperion::Components component, bool state) { - _componentRegister->setNewComponentState(component, state); + if (_componentRegister.isNull()) + { + Debug(_log, "ComponentRegister is not initialized, cannot set state for component '%s'", componentToString(component)); + } + + emit isSetNewComponentState(component, state); } std::map Hyperion::getAllComponents() const @@ -368,7 +396,7 @@ void Hyperion::setIdle(bool isIdle) clear(-1); bool const enable = !isIdle; - emit compStateChangeRequestAll(enable, {hyperion::COMP_LEDDEVICE, hyperion::COMP_SMOOTHING} ); + emit compStateChangeRequestAll(enable, { hyperion::COMP_LEDDEVICE, hyperion::COMP_SMOOTHING }); } void Hyperion::registerInput(int priority, hyperion::Components component, const QString& origin, const QString& owner, unsigned smooth_cfg) @@ -378,20 +406,20 @@ void Hyperion::registerInput(int priority, hyperion::Components component, const bool Hyperion::setInput(int priority, const std::vector& ledColors, int timeout_ms, bool clearEffect) { - if(_muxer->setInput(priority, ledColors, timeout_ms)) + if (_muxer->setInput(priority, ledColors, timeout_ms)) { - #if defined(ENABLE_EFFECTENGINE) +#if defined(ENABLE_EFFECTENGINE) // clear effect if this call does not come from an effect - if(clearEffect) + if (clearEffect) { _effectEngine->channelCleared(priority); } - #endif +#endif // if this priority is visible, update immediately - if(priority == _muxer->getCurrentPriority()) + if (priority == _muxer->getCurrentPriority()) { - refreshUpdate(); + update(); } return true; @@ -407,20 +435,20 @@ bool Hyperion::setInputImage(int priority, const Image& image, int64_t return false; } - if(_muxer->setInputImage(priority, image, timeout_ms)) + if (_muxer->setInputImage(priority, image, timeout_ms)) { - #if defined(ENABLE_EFFECTENGINE) +#if defined(ENABLE_EFFECTENGINE) // clear effect if this call does not come from an effect - if(clearEffect) + if (clearEffect) { _effectEngine->channelCleared(priority); } - #endif +#endif // if this priority is visible, update immediately - if(priority == _muxer->getCurrentPriority()) + if (priority == _muxer->getCurrentPriority()) { - refreshUpdate(); + update(); } return true; @@ -433,15 +461,15 @@ bool Hyperion::setInputInactive(int priority) return _muxer->setInputInactive(priority); } -void Hyperion::setColor(int priority, const std::vector &ledColors, int timeout_ms, const QString &origin, bool clearEffects) +void Hyperion::setColor(int priority, const std::vector& ledColors, int timeout_ms, const QString& origin, bool clearEffects) { - #if defined(ENABLE_EFFECTENGINE) +#if defined(ENABLE_EFFECTENGINE) // clear effect if this call does not come from an effect if (clearEffects) { _effectEngine->channelCleared(priority); } - #endif +#endif // create full led vector from single/multiple colors std::vector newLedColors; @@ -485,7 +513,7 @@ QStringList Hyperion::getAdjustmentIds() const return _raw2ledAdjustment->getAdjustmentIds(); } -ColorAdjustment * Hyperion::getAdjustment(const QString& identifier) const +ColorAdjustment* Hyperion::getAdjustment(const QString& identifier) const { return _raw2ledAdjustment->getAdjustment(identifier); } @@ -503,20 +531,20 @@ bool Hyperion::clear(int priority, bool forceClearAll) { _muxer->clearAll(forceClearAll); - #if defined(ENABLE_EFFECTENGINE) +#if defined(ENABLE_EFFECTENGINE) // send clearall signal to the effect engine _effectEngine->allChannelsCleared(); - #endif +#endif isCleared = true; } else { - #if defined(ENABLE_EFFECTENGINE) +#if defined(ENABLE_EFFECTENGINE) // send clear signal to the effect engine // (outside the check so the effect gets cleared even when the effect is not sending colors) _effectEngine->channelCleared(priority); - #endif +#endif if (_muxer->clearInput(priority)) { @@ -543,7 +571,7 @@ QList Hyperion::getActivePriorities() const Hyperion::InputsMap Hyperion::getPriorityInfo() const { - return _muxer->getInputInfo(); + return _muxer->getInputInfo(); } Hyperion::InputInfo Hyperion::getPriorityInfo(int priority) const @@ -557,12 +585,12 @@ std::list Hyperion::getActiveEffects() const return _effectEngine->getActiveEffects(); } -int Hyperion::setEffect(const QString &effectName, int priority, int timeout, const QString & origin) +int Hyperion::setEffect(const QString& effectName, int priority, int timeout, const QString& origin) { return _effectEngine->runEffect(effectName, priority, timeout, origin); } -int Hyperion::setEffect(const QString &effectName, const QJsonObject &args, int priority, int timeout, const QString &pythonScript, const QString &origin, const QString &imageData) +int Hyperion::setEffect(const QString& effectName, const QJsonObject& args, int priority, int timeout, const QString& pythonScript, const QString& origin, const QString& imageData) { return _effectEngine->runEffect(effectName, args, priority, timeout, pythonScript, origin, 0, imageData); } @@ -570,7 +598,7 @@ int Hyperion::setEffect(const QString &effectName, const QJsonObject &args, int void Hyperion::setLedMappingType(int mappingType) { - if(mappingType != _imageProcessor->getUserLedMappingType()) + if (mappingType != _imageProcessor->getUserLedMappingType()) { _imageProcessor->setLedMappingType(mappingType); emit imageToLedsMappingChanged(mappingType); @@ -608,28 +636,28 @@ void Hyperion::handleSourceAvailability(int priority) { int const previousPriority = _muxer->getPreviousPriority(); - if ( priority == PriorityMuxer::LOWEST_PRIORITY) + if (priority == PriorityMuxer::LOWEST_PRIORITY) { // Keep LED-device on, as background effect will kick-in shortly if (!_BGEffectHandler->_isEnabled()) { - Debug(_log,"No source left -> Pause output processing and switch LED-Device off"); + Debug(_log, "No source left -> Pause output processing and switch LED-Device off"); emit _ledDeviceWrapper->switchOff(); _deviceSmooth->setPause(true); } } else { - if ( previousPriority == PriorityMuxer::LOWEST_PRIORITY ) + if (previousPriority == PriorityMuxer::LOWEST_PRIORITY) { - if(_ledDeviceWrapper->isEnabled()) + if (_ledDeviceWrapper->isEnabled()) { - Debug(_log,"new source available -> LED-Device is enabled, switch LED-device on and resume output processing"); + Debug(_log, "new source available -> LED-Device is enabled, switch LED-device on and resume output processing"); emit _ledDeviceWrapper->switchOn(); } else { - Debug(_log,"new source available -> LED-Device not enabled, cannot switch on LED-device"); + Debug(_log, "new source available -> LED-Device not enabled, cannot switch on LED-device"); } _deviceSmooth->setPause(false); } @@ -648,15 +676,23 @@ void Hyperion::update() int const priority = _muxer->getCurrentPriority(); const PriorityMuxer::InputInfo priorityInfo = _muxer->getInputInfo(priority); - std::vector ledColors; - // copy image & process OR copy ledColors from muxer Image const image = priorityInfo.image; + + if (image.size() == 0) + { + //TO DO + qDebug() << "Empty image - skip update"; + return; + } + + std::vector ledColors; + if (image.width() > 1 || image.height() > 1) { - _imageEmissionInterval = (image.width() > 1280) ? 2 * DEFAULT_MAX_IMAGE_EMISSION_INTERVAL : DEFAULT_MAX_IMAGE_EMISSION_INTERVAL; + _imageEmissionInterval = (image.width() > 1280) ? 2 * DEFAULT_MAX_IMAGE_EMISSION_INTERVAL : DEFAULT_MAX_IMAGE_EMISSION_INTERVAL; // Throttle the emission of currentImage(image) signal - qint64 elapsedImageEmissionTime = _imageTimer.elapsed(); + qint64 const elapsedImageEmissionTime = _imageTimer.elapsed(); if (elapsedImageEmissionTime - _lastImageEmission >= _imageEmissionInterval.count()) { _lastImageEmission = elapsedImageEmissionTime; @@ -671,7 +707,7 @@ void Hyperion::update() { for (unsigned long const id : _ledString.blacklistedLedIds()) { - if (id > ledColors.size()-1) + if (id > ledColors.size() - 1) { break; } @@ -694,7 +730,7 @@ void Hyperion::update() assert(ledColors.size() >= _ledStringColorOrder.size()); // Only apply color order for LEDs defined by layout - for (size_t i=0; i < _ledStringColorOrder.size(); ++i) + for (size_t i = 0; i < _ledStringColorOrder.size(); ++i) { ColorRgb& color = ledColors.at(i); // correct the color byte order @@ -730,7 +766,7 @@ void Hyperion::update() if (_ledDeviceWrapper->isOn()) { // Smoothing is disabled - if (! _deviceSmooth->enabled()) + if (!_deviceSmooth->enabled()) { // Throttle the emission of LED-Device data signal qint64 elapsedLedDeviceDataEmissionTime = _ledDeviceDataTimer.elapsed(); @@ -742,8 +778,8 @@ void Hyperion::update() } else { - // feed smoothing in pause mode to maintain a smooth transition back to smooth mode - if (_deviceSmooth->enabled() || _deviceSmooth->pause()) + // device is enabled, feed smoothing in pause mode to maintain a smooth transition back to smooth mode + if (!_deviceSmooth->pause()) { _deviceSmooth->updateLedValues(_ledBuffer); } diff --git a/libsrc/hyperion/HyperionIManager.cpp b/libsrc/hyperion/HyperionIManager.cpp index c4b81c200..3efea7a4a 100644 --- a/libsrc/hyperion/HyperionIManager.cpp +++ b/libsrc/hyperion/HyperionIManager.cpp @@ -313,43 +313,44 @@ void HyperionIManager::handleFinished(const QString& name) { Hyperion* rawHyperion = qobject_cast(sender()); - if (rawHyperion != nullptr) + if (rawHyperion) { - quint8 const instanceId = rawHyperion->getInstanceIndex(); + QSharedPointer hyperion = rawHyperion->sharedFromThis(); - QSharedPointer hyperion; + quint8 const instanceId = hyperion->getInstanceIndex(); if (_startingInstances.contains(instanceId)) { - hyperion = _startingInstances.value(instanceId); + _startingInstances.remove(instanceId); } else if (_runningInstances.contains(instanceId)) { - hyperion = _runningInstances.value(instanceId); + _runningInstances.remove(instanceId); } - if (!hyperion.isNull()) - { - // Manually stop the thread and cleanup - QThread* thread = hyperion->thread(); - if (thread != nullptr) - { - thread->quit(); - thread->wait(); - } + emit instanceStateChanged(InstanceState::H_STOPPED, instanceId); + emit change(); - Info(_log,"Hyperion instance [%u] - '%s' stopped.", instanceId, QSTRING_CSTR(name)); + qDebug() << "Hyperion instance [" << instanceId << "] - '" << name << "' finished."; - _startingInstances.remove(instanceId); - _runningInstances.remove(instanceId); - - emit instanceStateChanged(InstanceState::H_STOPPED, instanceId); - emit change(); + // Manually stop the thread and cleanup + QThread* thread = hyperion->thread(); + if (thread != nullptr) + { + thread->quit(); + thread->wait(); } + + Info(_log, "Hyperion instance [%u] - '%s' stopped.", instanceId, QSTRING_CSTR(name)); + + } + else + { + Info(_log, "No Hyperion instances are running."); } - if ( _runningInstances.isEmpty()) + if (_runningInstances.isEmpty()) { - Info(_log,"All Hyperion instances are stopped now."); + Info(_log, "All Hyperion instances are stopped now."); emit areAllInstancesStopped(); } } diff --git a/libsrc/hyperion/ImageProcessor.cpp b/libsrc/hyperion/ImageProcessor.cpp index 7af90eec8..0934ba6ae 100644 --- a/libsrc/hyperion/ImageProcessor.cpp +++ b/libsrc/hyperion/ImageProcessor.cpp @@ -10,6 +10,8 @@ #include #include +#include "utils/TrackedMemory.h" + using namespace hyperion; void ImageProcessor::registerProcessingUnit( @@ -20,7 +22,7 @@ void ImageProcessor::registerProcessingUnit( { if (width > 0 && height > 0) { - _imageToLedColors = QSharedPointer(new ImageToLedsMap( + _imageToLedColors = MAKE_TRACKED_SHARED(ImageToLedsMap, _log, width, height, @@ -29,11 +31,11 @@ void ImageProcessor::registerProcessingUnit( _ledString.leds(), _reducedPixelSetFactorFactor, _accuraryLevel - )); + ); } else { - _imageToLedColors = QSharedPointer(nullptr); + _imageToLedColors = MAKE_TRACKED_SHARED(ImageToLedsMap, _log, 0, 0, 0, 0, _ledString.leds()); } } @@ -105,30 +107,39 @@ QString ImageProcessor::mappingTypeToStr(int mappingType) return typeText; } -ImageProcessor::ImageProcessor(const LedString& ledString, Hyperion* hyperion) - : QObject(hyperion) +ImageProcessor::ImageProcessor(const LedString& ledString, const QSharedPointer& hyperionInstance) + : QObject() + , _hyperionWeak(hyperionInstance) , _log(nullptr) , _ledString(ledString) - , _borderProcessor(new BlackBorderProcessor(hyperion, this)) + , _borderProcessor(nullptr) , _imageToLedColors(nullptr) , _mappingType(0) , _userMappingType(0) , _hardMappingType(-1) , _accuraryLevel(0) , _reducedPixelSetFactorFactor(1) - , _hyperion(hyperion) { - QString subComponent = hyperion->property("instance").toString(); - _log= Logger::getInstance("IMAGETOLED", subComponent); + QString subComponent{ "__" }; + + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + } + _log = Logger::getInstance("IMAGETOLED", subComponent); + + _borderProcessor.reset(new BlackBorderProcessor(hyperion, this)); // init - handleSettingsUpdate(settings::COLOR, _hyperion->getSetting(settings::COLOR)); + handleSettingsUpdate(settings::COLOR, hyperion->getSetting(settings::COLOR)); // listen for changes in color - ledmapping - connect(_hyperion, &Hyperion::settingsChanged, this, &ImageProcessor::handleSettingsUpdate); + connect(hyperion.get(), &Hyperion::settingsChanged, this, &ImageProcessor::handleSettingsUpdate); } ImageProcessor::~ImageProcessor() { + qDebug() << "ImageProcessor::~ImageProcessor()..."; } void ImageProcessor::handleSettingsUpdate(settings::type type, const QJsonDocument& config) diff --git a/libsrc/hyperion/ImageToLedsMap.cpp b/libsrc/hyperion/ImageToLedsMap.cpp index f3e3ac240..fdfa84c4e 100644 --- a/libsrc/hyperion/ImageToLedsMap.cpp +++ b/libsrc/hyperion/ImageToLedsMap.cpp @@ -114,6 +114,11 @@ ImageToLedsMap::ImageToLedsMap( } + ImageToLedsMap::~ImageToLedsMap() + { + qDebug() << "ImageToLedsMap::~ImageToLedsMap()..."; + } + int ImageToLedsMap::width() const { return _width; diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 35b8e48ac..3c7812e11 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -58,12 +58,12 @@ const unsigned DEFAULT_OUTPUTDEPLAY = 0; // in frames using namespace hyperion; -LinearColorSmoothing::LinearColorSmoothing(const QJsonObject &config, Hyperion *hyperion) - : QObject(hyperion) +LinearColorSmoothing::LinearColorSmoothing(const QJsonObject &config, const QSharedPointer& hyperionInstance) + : QObject() , _smoothConfig(config) , _log(nullptr) - , _hyperion(hyperion) - , _prioMuxer(_hyperion->getMuxerInstance()) + , _hyperionWeak(hyperionInstance) + , _prioMuxerWeak(nullptr) , _updateInterval(DEFAULT_UPDATEINTERVALL.count()) , _settlingTime(DEFAULT_SETTLINGTIME) , _timer(nullptr) @@ -75,7 +75,13 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonObject &config, Hyperion * , _smoothingType(SmoothingType::Linear) , tempValues(std::vector()) { - QString subComponent = hyperion->property("instance").toString(); + QString subComponent{ "__" }; + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + _prioMuxerWeak = hyperion->getMuxerInstance(); + } _log= Logger::getInstance("SMOOTHING", subComponent); // init cfg (default) @@ -86,12 +92,14 @@ LinearColorSmoothing::LinearColorSmoothing(const QJsonObject &config, Hyperion * } LinearColorSmoothing::~LinearColorSmoothing() { + qDebug() << "LinearColorSmoothing::~LinearColorSmoothing()..."; } void LinearColorSmoothing::start() { Info(_log, "LinearColorSmoothing starting..."); + _timer.reset(new QTimer(this)); _timer->setTimerType(Qt::PreciseTimer); @@ -99,8 +107,8 @@ void LinearColorSmoothing::start() setPause(true); // listen for comp changes - QObject::connect(_hyperion, &Hyperion::compStateChangeRequest, this, &LinearColorSmoothing::componentStateChange); - QObject::connect(_prioMuxer.get(), &PriorityMuxer::prioritiesChanged, this, &LinearColorSmoothing::handlePriorityUpdate); + QObject::connect(_hyperionWeak.toStrongRef().get(), &Hyperion::compStateChangeRequest, this, &LinearColorSmoothing::componentStateChange); + QObject::connect(_prioMuxerWeak.toStrongRef().get(), &PriorityMuxer::prioritiesChanged, this, &LinearColorSmoothing::handlePriorityUpdate); connect(_timer.get(), &QTimer::timeout, this, &LinearColorSmoothing::updateLeds); updateSettings(_smoothConfig); @@ -110,7 +118,8 @@ void LinearColorSmoothing::stop() { Debug(_log, "LinearColorSmoothing stopping..."); - QObject::disconnect(_prioMuxer.get(), &PriorityMuxer::prioritiesChanged, this, &LinearColorSmoothing::handlePriorityUpdate); + QObject::disconnect(_prioMuxerWeak.toStrongRef().get(), &PriorityMuxer::prioritiesChanged, this, &LinearColorSmoothing::handlePriorityUpdate); + setEnable(false); _timer->stop(); @@ -163,7 +172,7 @@ void LinearColorSmoothing::handleSettingsUpdate(settings::type type, const QJson void LinearColorSmoothing::handlePriorityUpdate(int priority) { - const PriorityMuxer::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority); + const PriorityMuxer::InputInfo priorityInfo = _prioMuxerWeak.toStrongRef()->getInputInfo(priority); int smooth_cfg = priorityInfo.smooth_cfg; if (smooth_cfg != _currentConfigId || smooth_cfg == SmoothingConfigID::EFFECT_DYNAMIC) { @@ -544,7 +553,11 @@ void LinearColorSmoothing::queueColors(const std::vector &ledColors) // No output delay => immediate write if (!_pause) { - emit _hyperion->ledDeviceData(ledColors); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + emit hyperion->ledDeviceData(ledColors); + } } } else @@ -559,7 +572,11 @@ void LinearColorSmoothing::queueColors(const std::vector &ledColors) { if (!_pause) { - emit _hyperion->ledDeviceData(_outputQueue.front()); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + emit hyperion->ledDeviceData(_outputQueue.front()); + } } _outputQueue.pop_front(); } @@ -595,7 +612,11 @@ void LinearColorSmoothing::setEnable(bool enable) clearQueuedColors(); } // update comp register - _hyperion->setNewComponentState(hyperion::COMP_SMOOTHING, enable); + QSharedPointer hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + hyperion->setNewComponentState(hyperion::COMP_SMOOTHING, enable); + } } } diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index 34cb47326..45ee80e0f 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -27,9 +27,9 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) , _manualSelectedPriority(MANUAL_SELECTED_PRIORITY) , _prevVisComp (hyperion::Components::COMP_COLOR) , _sourceAutoSelectEnabled(true) - , _updateTimer(new QTimer(this)) - , _timer(new QTimer(this)) - , _blockTimer(new QTimer(this)) + , _updateTimer(nullptr) + , _timer(nullptr) + , _blockTimer(nullptr) { QString subComponent = parent->property("instance").toString(); _log= Logger::getInstance("MUXER", subComponent); @@ -50,6 +50,7 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) PriorityMuxer::~PriorityMuxer() { + qDebug() << "PriorityMuxer::~PriorityMuxer()..."; } void PriorityMuxer::start() diff --git a/libsrc/hyperion/schema/schema-network.json b/libsrc/hyperion/schema/schema-network.json index c1c83516d..92cd988be 100644 --- a/libsrc/hyperion/schema/schema-network.json +++ b/libsrc/hyperion/schema/schema-network.json @@ -2,47 +2,12 @@ "type": "object", "title": "edt_conf_network_heading_title", "properties": { - "internetAccessAPI": { - "type": "boolean", - "title": "edt_conf_network_internetAccessAPI_title", - "required": true, - "default": false, - "propertyOrder": 1 - }, - "restirctedInternetAccessAPI": { - "type": "boolean", - "title": "edt_conf_network_restirctedInternetAccessAPI_title", - "required": true, - "default": false, - "options": { - "dependencies": { - "internetAccessAPI": true - } - }, - "propertyOrder": 2 - }, - "ipWhitelist": { - "type": "array", - "title": "edt_conf_network_ipWhitelist_title", - "required": true, - "items": { - "type": "string", - "title": "edt_conf_network_ip_itemtitle", - "allowEmptyArray": true - }, - "options": { - "dependencies": { - "restirctedInternetAccessAPI": true - } - }, - "propertyOrder": 3 - }, "localApiAuth": { "type": "boolean", "title": "edt_conf_network_localApiAuth_title", "required": true, "default": false, - "propertyOrder": 4 + "propertyOrder": 1 } }, "additionalProperties": false diff --git a/libsrc/jsonserver/JsonServer.cpp b/libsrc/jsonserver/JsonServer.cpp index 64d08ff66..daf9358a0 100644 --- a/libsrc/jsonserver/JsonServer.cpp +++ b/libsrc/jsonserver/JsonServer.cpp @@ -100,17 +100,12 @@ void JsonServer::newConnection() { if (QTcpSocket * socket = _server->nextPendingConnection()) { - if(_netOrigin->accessAllowed(socket->peerAddress(), socket->localAddress())) - { - Debug(_log, "New connection from: %s",QSTRING_CSTR(socket->peerAddress().toString())); - JsonClientConnection * connection = new JsonClientConnection(socket, _netOrigin->isLocalAddress(socket->peerAddress(), socket->localAddress())); - _openConnections.insert(connection); - - // register slot for cleaning up after the connection closed - connect(connection, &JsonClientConnection::connectionClosed, this, &JsonServer::closedConnection); - } - else - socket->close(); + Debug(_log, "New connection from: %s",QSTRING_CSTR(socket->peerAddress().toString())); + JsonClientConnection * connection = new JsonClientConnection(socket, _netOrigin->isLocalAddress(socket->peerAddress(), socket->localAddress())); + _openConnections.insert(connection); + + // register slot for cleaning up after the connection closed + connect(connection, &JsonClientConnection::connectionClosed, this, &JsonServer::closedConnection); } } } diff --git a/libsrc/leddevice/LedDevice.cpp b/libsrc/leddevice/LedDevice.cpp index 0bd3135e9..5ae70d7e5 100644 --- a/libsrc/leddevice/LedDevice.cpp +++ b/libsrc/leddevice/LedDevice.cpp @@ -70,6 +70,7 @@ LedDevice::LedDevice(const QJsonObject& deviceConfig, QObject* parent) LedDevice::~LedDevice() { + qDebug() << "LedDevice::~LedDevice()..."; } void LedDevice::start() @@ -157,7 +158,10 @@ void LedDevice::enable() if (!_isDeviceReady) { - open(); + if (open() < 0) + { + setInError("Failed to open device", _isDeviceRecoverable); + } } bool isEnableFailed(true); diff --git a/libsrc/leddevice/LedDeviceWrapper.cpp b/libsrc/leddevice/LedDeviceWrapper.cpp index 52b738390..73ad7b8f0 100644 --- a/libsrc/leddevice/LedDeviceWrapper.cpp +++ b/libsrc/leddevice/LedDeviceWrapper.cpp @@ -21,22 +21,31 @@ LedDeviceRegistry LedDeviceWrapper::_ledDeviceMap {}; static std::once_flag initFlag; -LedDeviceWrapper::LedDeviceWrapper(Hyperion* hyperion) - : QObject(hyperion) +LedDeviceWrapper::LedDeviceWrapper(const QSharedPointer& hyperionInstance) + : QObject() , _log(nullptr) - , _hyperion(hyperion) + , _hyperionWeak(hyperionInstance) , _ledDevice(nullptr) , _isEnabled(false) , _isOn(false) { - QString const subComponent = parent()->property("instance").toString(); + QString subComponent{ "__" }; + + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + subComponent = hyperion->property("instance").toString(); + + hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, false); + } _log = Logger::getInstance("LEDDEVICE", subComponent); - _hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, false); + } LedDeviceWrapper::~LedDeviceWrapper() { + qDebug() << "LedDeviceWrapper::~LedDeviceWrapper()..."; } void LedDeviceWrapper::createLedDevice(const QJsonObject& config) @@ -85,7 +94,11 @@ void LedDeviceWrapper::handleComponentState(hyperion::Components component, bool void LedDeviceWrapper::onIsEnabledChanged(bool isEnabled) { - _hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, isEnabled); + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + hyperion->setNewComponentState(hyperion::COMP_LEDDEVICE, isEnabled); + } _isEnabled = isEnabled; } @@ -94,7 +107,11 @@ void LedDeviceWrapper::onIsOnChanged(bool isOn) _isOn = isOn; if (_isOn) { - _hyperion->refreshUpdate(); + QSharedPointer const hyperion = _hyperionWeak.toStrongRef(); + if (hyperion) + { + hyperion->refreshUpdate(); + } } } @@ -116,12 +133,15 @@ void LedDeviceWrapper::stopDevice() emit stop(); loop.exec(); + _ledDevice.reset(); + if (!_ledDeviceThread.isNull()) { if (_ledDeviceThread->isRunning()) { _ledDeviceThread->quit(); _ledDeviceThread->wait(); + _ledDeviceThread.reset(); } } diff --git a/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp b/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp index 08ef0f0a6..463511940 100644 --- a/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp +++ b/libsrc/leddevice/dev_hid/LedDeviceLightpack.cpp @@ -82,7 +82,7 @@ bool LedDeviceLightpack::init(const QJsonObject &deviceConfig) { Debug(_log, "USB context initialized"); - if ( _log->getMinLevel() == Logger::LogLevel::DEBUG ) + if ( _log->getMinLevel() == Logger::LogLevel::LOG_DEBUG ) { int logLevel = LIBUSB_LOG_LEVEL_INFO; #if LIBUSB_API_VERSION >= 0x01000106 diff --git a/libsrc/leddevice/dev_net/LedDeviceCololight.cpp b/libsrc/leddevice/dev_net/LedDeviceCololight.cpp index 1ac5cee6e..01aa80dc4 100644 --- a/libsrc/leddevice/dev_net/LedDeviceCololight.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceCololight.cpp @@ -168,6 +168,7 @@ int LedDeviceCololight::open() int retval = -1; _isDeviceReady = false; + this->setIsRecoverable(true); if (NetUtils::resolveHostToAddress(_log, _hostName, _address)) { if (ProviderUdp::open() == 0) diff --git a/libsrc/leddevice/dev_net/LedDeviceHomeAssistant.cpp b/libsrc/leddevice/dev_net/LedDeviceHomeAssistant.cpp index 1c5008817..522bfed55 100644 --- a/libsrc/leddevice/dev_net/LedDeviceHomeAssistant.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceHomeAssistant.cpp @@ -166,6 +166,12 @@ bool LedDeviceHomeAssistant::openRestAPI() { bool isInitOK{ true }; + if (_address.isNull()) + { + Error(_log, "Empty IP address. REST API cannot be initiatised."); + return false; + } + if (_restApi == nullptr) { if (_apiPort == 0) @@ -190,6 +196,7 @@ int LedDeviceHomeAssistant::open() int retval = -1; _isDeviceReady = false; + this->setIsRecoverable(true); if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort)) { if (openRestAPI()) diff --git a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp index 596cb8d31..88938dcae 100644 --- a/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceNanoleaf.cpp @@ -404,6 +404,12 @@ bool LedDeviceNanoleaf::openRestAPI() { bool isInitOK{ true }; + if (_address.isNull()) + { + Error(_log, "Empty IP address. REST API cannot be initiatised."); + return false; + } + if (_restApi == nullptr) { _restApi = new ProviderRestApi(_address.toString(), _apiPort); @@ -420,6 +426,7 @@ int LedDeviceNanoleaf::open() int retval = -1; _isDeviceReady = false; + this->setIsRecoverable(true); if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort)) { if (openRestAPI()) diff --git a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp index 08a091220..b81e5a9ce 100644 --- a/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp +++ b/libsrc/leddevice/dev_net/LedDevicePhilipsHue.cpp @@ -413,6 +413,12 @@ bool LedDevicePhilipsHueBridge::openRestAPI() { bool isInitOK {true}; + if (_address.isNull()) + { + Error(_log, "Empty IP address. REST API cannot be initiatised."); + return false; + } + if (_restApi == nullptr) { _restApi = new ProviderRestApi(_address.toString(), _apiPort); @@ -507,6 +513,7 @@ int LedDevicePhilipsHueBridge::open() int retval = -1; _isDeviceReady = false; + this->setIsRecoverable(true); if (NetUtils::resolveHostToAddress(_log, _hostName, _address)) { if ( openRestAPI() ) @@ -1902,6 +1909,10 @@ bool LedDevicePhilipsHue::initLeds() if( _useEntertainmentAPI ) { _groupName = getGroupName( _groupId ); + if (!_groupName.isEmpty()) + { + isInitOK = true; + } } else { @@ -1909,13 +1920,14 @@ bool LedDevicePhilipsHue::initLeds() setLatchTime( 100 * getLightsCount() ); isInitOK = true; } - _isInitLeds = true; } else { isInitOK = false; } } + _isInitLeds = isInitOK; + return isInitOK; } diff --git a/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp b/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp index 261643cab..20e12ae46 100644 --- a/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceTpm2net.cpp @@ -51,6 +51,7 @@ int LedDeviceTpm2net::open() int retval = -1; _isDeviceReady = false; + this->setIsRecoverable(true); if (NetUtils::resolveHostToAddress(_log, _hostName, _address)) { if (ProviderUdp::open() == 0) diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpDdp.cpp b/libsrc/leddevice/dev_net/LedDeviceUdpDdp.cpp index a5e362826..990d1c097 100644 --- a/libsrc/leddevice/dev_net/LedDeviceUdpDdp.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceUdpDdp.cpp @@ -97,19 +97,36 @@ bool LedDeviceUdpDdp::init(const QJsonObject &deviceConfig) } int LedDeviceUdpDdp::open() +{ + QHostAddress resolvedAddress; + this->setIsRecoverable(true); + if (NetUtils::resolveHostToAddress(_log, _hostName, resolvedAddress)) + { + return open(resolvedAddress); + } + + return -1; +} + +int LedDeviceUdpDdp::open(const QHostAddress& address) { int retval = -1; _isDeviceReady = false; - if (NetUtils::resolveHostToAddress(_log, _hostName, _address)) + if (address.isNull()) { - if (ProviderUdp::open() == 0) - { - // Everything is OK, device is ready - _isDeviceReady = true; - retval = 0; - } + Error(_log, "Empty IP address. UDP stream cannot be initiatised."); + return retval; + } + + _address = address; + if (ProviderUdp::open() == 0) + { + // Everything is OK, device is ready + _isDeviceReady = true; + retval = 0; } + return retval; } diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpDdp.h b/libsrc/leddevice/dev_net/LedDeviceUdpDdp.h index 623700a41..834bba9b5 100644 --- a/libsrc/leddevice/dev_net/LedDeviceUdpDdp.h +++ b/libsrc/leddevice/dev_net/LedDeviceUdpDdp.h @@ -44,6 +44,14 @@ class LedDeviceUdpDdp : public virtual ProviderUdp /// int open() override; + /// + /// @brief Opens the output device for a given host address + /// + /// @param[in] address the device's network address + /// @return Zero on success (i.e. device is ready), else negative + /// + int open(const QHostAddress& address); + /// /// @brief Writes the RGB-Color values to the LEDs. /// diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp index 0f0d167fd..562908b42 100644 --- a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.cpp @@ -50,19 +50,36 @@ bool LedDeviceUdpRaw::init(const QJsonObject &deviceConfig) } int LedDeviceUdpRaw::open() +{ + QHostAddress resolvedAddress; + this->setIsRecoverable(true); + if (NetUtils::resolveHostToAddress(_log, _hostName, resolvedAddress)) + { + return open(resolvedAddress); + } + + return -1; +} + +int LedDeviceUdpRaw::open(const QHostAddress& address) { int retval = -1; _isDeviceReady = false; - if (NetUtils::resolveHostToAddress(_log, _hostName, _address)) + if (address.isNull()) { - if (ProviderUdp::open() == 0) - { - // Everything is OK, device is ready - _isDeviceReady = true; - retval = 0; - } + Error(_log, "Empty IP address. UDP stream cannot be initiatised."); + return retval; + } + + _address = address; + if (ProviderUdp::open() == 0) + { + // Everything is OK, device is ready + _isDeviceReady = true; + retval = 0; } + return retval; } diff --git a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h index 3a63518e4..3b72eb65c 100644 --- a/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h +++ b/libsrc/leddevice/dev_net/LedDeviceUdpRaw.h @@ -51,6 +51,14 @@ class LedDeviceUdpRaw : public virtual ProviderUdp /// int open() override; + /// + /// @brief Opens the output device for a given host address + /// + /// @param[in] address the device's network address + /// @return Zero on success (i.e. device is ready), else negative + /// + int open(const QHostAddress& address); + /// /// @brief Writes the RGB-Color values to the LEDs. /// diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.cpp b/libsrc/leddevice/dev_net/LedDeviceWled.cpp index 9de8a5fa2..763946cc5 100644 --- a/libsrc/leddevice/dev_net/LedDeviceWled.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceWled.cpp @@ -177,7 +177,11 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig) bool LedDeviceWled::openRestAPI() { - bool isInitOK {true}; + if (_address.isNull()) + { + Error(_log, "Empty IP address. REST API cannot be initiatised."); + return false; + } if ( _restApi == nullptr ) { @@ -192,7 +196,7 @@ bool LedDeviceWled::openRestAPI() _restApi->setPort(_apiPort); } - return isInitOK; + return true; } int LedDeviceWled::open() @@ -200,13 +204,14 @@ int LedDeviceWled::open() int retval = -1; _isDeviceReady = false; + this->setIsRecoverable(true); if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort)) { if ( openRestAPI() ) { if (_isStreamDDP) { - if (LedDeviceUdpDdp::open() == 0) + if (LedDeviceUdpDdp::open(_address) == 0) { // Everything is OK, device is ready _isDeviceReady = true; @@ -215,7 +220,7 @@ int LedDeviceWled::open() } else { - if (LedDeviceUdpRaw::open() == 0) + if (LedDeviceUdpRaw::open(_address) == 0) { // Everything is OK, device is ready _isDeviceReady = true; diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.h b/libsrc/leddevice/dev_net/LedDeviceWled.h index 7f6b25ed1..c9051e935 100644 --- a/libsrc/leddevice/dev_net/LedDeviceWled.h +++ b/libsrc/leddevice/dev_net/LedDeviceWled.h @@ -160,7 +160,6 @@ class LedDeviceWled : public LedDeviceUdpDdp, LedDeviceUdpRaw ///REST-API wrapper ProviderRestApi* _restApi; - QString _hostAddress; int _apiPort; QJsonObject _wledInfo; diff --git a/libsrc/protoserver/ProtoServer.cpp b/libsrc/protoserver/ProtoServer.cpp index 0300d868b..8f50956b1 100644 --- a/libsrc/protoserver/ProtoServer.cpp +++ b/libsrc/protoserver/ProtoServer.cpp @@ -68,22 +68,17 @@ void ProtoServer::newConnection() { if(QTcpSocket * socket = _server->nextPendingConnection()) { - if(_netOrigin->accessAllowed(socket->peerAddress(), socket->localAddress())) - { - Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); - ProtoClientConnection * client = new ProtoClientConnection(socket, _timeout, this); - // internal - connect(client, &ProtoClientConnection::clientDisconnected, this, &ProtoServer::clientDisconnected); - connect(client, &ProtoClientConnection::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput); - connect(client, &ProtoClientConnection::clearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput); - connect(client, &ProtoClientConnection::setGlobalInputImage, GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage); - connect(client, &ProtoClientConnection::setGlobalInputColor, GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor); - connect(client, &ProtoClientConnection::setBufferImage, GlobalSignals::getInstance(), &GlobalSignals::setBufferImage); - connect(GlobalSignals::getInstance(), &GlobalSignals::globalRegRequired, client, &ProtoClientConnection::registationRequired); - _openConnections.append(client); - } - else - socket->close(); + Debug(_log, "New connection from %s", QSTRING_CSTR(socket->peerAddress().toString())); + ProtoClientConnection * client = new ProtoClientConnection(socket, _timeout, this); + // internal + connect(client, &ProtoClientConnection::clientDisconnected, this, &ProtoServer::clientDisconnected); + connect(client, &ProtoClientConnection::registerGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::registerGlobalInput); + connect(client, &ProtoClientConnection::clearGlobalInput, GlobalSignals::getInstance(), &GlobalSignals::clearGlobalInput); + connect(client, &ProtoClientConnection::setGlobalInputImage, GlobalSignals::getInstance(), &GlobalSignals::setGlobalImage); + connect(client, &ProtoClientConnection::setGlobalInputColor, GlobalSignals::getInstance(), &GlobalSignals::setGlobalColor); + connect(client, &ProtoClientConnection::setBufferImage, GlobalSignals::getInstance(), &GlobalSignals::setBufferImage); + connect(GlobalSignals::getInstance(), &GlobalSignals::globalRegRequired, client, &ProtoClientConnection::registationRequired); + _openConnections.append(client); } } } diff --git a/libsrc/utils/Logger.cpp b/libsrc/utils/Logger.cpp index 713352469..c7cd90966 100644 --- a/libsrc/utils/Logger.cpp +++ b/libsrc/utils/Logger.cpp @@ -18,33 +18,33 @@ #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QRecursiveMutex Logger::MapLock; +QRecursiveMutex Logger::MapLock; #else - QMutex Logger::MapLock{ QMutex::Recursive }; +QMutex Logger::MapLock{ QMutex::Recursive }; #endif -QMap Logger::LoggerMap { }; -QAtomicInteger Logger::GLOBAL_MIN_LOG_LEVEL { static_cast(Logger::UNSET)}; +QMap Logger::LoggerMap{ }; +QAtomicInteger Logger::GLOBAL_MIN_LOG_LEVEL{ static_cast(Logger::LogLevel::LOG_UNSET) }; namespace { -const char * LogLevelStrings[] = { "", "DEBUG", "INFO", "WARNING", "ERROR", "OFF" }; + const char* LogLevelStrings[] = { "", "DEBUG", "INFO", "WARNING", "ERROR", "OFF" }; #ifndef _WIN32 -const int LogLevelSysLog[] = { LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR }; + const int LogLevelSysLog[] = { LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR }; #endif -const size_t MAX_IDENTIFICATION_LENGTH = 22; + const size_t MAX_IDENTIFICATION_LENGTH = 22; -QAtomicInteger LoggerCount = 0; -QAtomicInteger LoggerId = 0; + QAtomicInteger LoggerCount = 0; + QAtomicInteger LoggerId = 0; -const int MAX_LOG_MSG_BUFFERED = 500; -const int MaxRepeatCountSize = 200; -QThreadStorage RepeatCount; -QThreadStorage RepeatMessage; + const int MAX_LOG_MSG_BUFFERED = 500; + const int MaxRepeatCountSize = 200; + QThreadStorage RepeatCount; + QThreadStorage RepeatMessage; } // namespace -Logger* Logger::getInstance(const QString & name, const QString & subName, Logger::LogLevel minLevel) +Logger* Logger::getInstance(const QString& name, const QString& subName, Logger::LogLevel minLevel) { QMutexLocker lock(&MapLock); @@ -59,13 +59,13 @@ Logger* Logger::getInstance(const QString & name, const QString & subName, Logge return log; } -void Logger::deleteInstance(const QString & name, const QString & subName) +void Logger::deleteInstance(const QString& name, const QString& subName) { QMutexLocker lock(&MapLock); if (name.isEmpty()) { - for (auto *logger : std::as_const(LoggerMap)) { + for (auto* logger : std::as_const(LoggerMap)) { logger->deleteLater(); } @@ -73,12 +73,12 @@ void Logger::deleteInstance(const QString & name, const QString & subName) } else { - LoggerMap.value(name + subName, nullptr)->deleteLater(); + LoggerMap.value(name + subName, nullptr)->deleteLater(); LoggerMap.remove(name + subName); } } -void Logger::setLogLevel(LogLevel level, const QString & name, const QString & subName) +void Logger::setLogLevel(LogLevel level, const QString& name, const QString& subName) { if (name.isEmpty()) { @@ -91,7 +91,7 @@ void Logger::setLogLevel(LogLevel level, const QString & name, const QString & s } } -Logger::LogLevel Logger::getLogLevel(const QString & name, const QString & subName) +Logger::LogLevel Logger::getLogLevel(const QString& name, const QString& subName) { if (name.isEmpty()) { @@ -102,9 +102,8 @@ Logger::LogLevel Logger::getLogLevel(const QString & name, const QString & subNa return log->getMinLevel(); } -Logger::Logger (const QString & name, const QString & subName, LogLevel minLevel) - : QObject() - , _name(name) +Logger::Logger(const QString& name, const QString& subName, LogLevel minLevel) + : _name(name) , _subName(subName) , _syslogEnabled(true) , _loggerId(LoggerId++) @@ -117,7 +116,7 @@ Logger::Logger (const QString & name, const QString & subName, LogLevel minLevel #ifndef _WIN32 if (_syslogEnabled) { - openlog (nullptr, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); + openlog(nullptr, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0); } #endif } @@ -136,10 +135,10 @@ Logger::~Logger() } } -void Logger::write(const Logger::T_LOG_MESSAGE & message) +void Logger::write(const Logger::T_LOG_MESSAGE& message) { QString location; - if (message.level == Logger::DEBUG) + if (message.level == LOG_DEBUG) { location = QString("%1:%2:%3() | ") .arg(message.fileName) @@ -151,24 +150,27 @@ void Logger::write(const Logger::T_LOG_MESSAGE & message) name.resize(MAX_IDENTIFICATION_LENGTH, ' '); const QDateTime timestamp = QDateTime::fromMSecsSinceEpoch(message.utime); - std::cout << QString("%1 %2 : <%3> %4%5") - .arg(timestamp.toString(Qt::ISODateWithMs)) - .arg(name) - .arg(LogLevelStrings[message.level]) - .arg(location) - .arg(message.message) - .toStdString() - << std::endl; + QString const msg = QString("%1 %2 : <%3> %4%5\n") + .arg(timestamp.toString(Qt::ISODateWithMs), name, LogLevelStrings[message.level], location, message.message); + +#ifdef _WIN32 + if (IsDebuggerPresent()) + { + OutputDebugStringA(QSTRING_CSTR(msg)); + } + else +#endif + std::cout << msg.toStdString(); emit newLogMessage(message); } void Logger::Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...) { - Logger::LogLevel globalLevel = static_cast(int(GLOBAL_MIN_LOG_LEVEL)); + LogLevel const globalLevel = LogLevel(int(GLOBAL_MIN_LOG_LEVEL)); - if ( (globalLevel == Logger::UNSET && level < _minLevel) // no global level, use level from logger - || (globalLevel > Logger::UNSET && level < globalLevel) ) // global level set, use global level + if ((globalLevel == Logger::LogLevel::LOG_UNSET && level < _minLevel) // no global level, use level from logger + || (globalLevel > LogLevel::LOG_UNSET && level < globalLevel)) // global level set, use global level { return; } @@ -176,31 +178,31 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u const size_t max_msg_length = 1024; char msg[max_msg_length]; va_list args; - va_start (args, fmt); - vsnprintf (msg, max_msg_length, fmt, args); - va_end (args); + va_start(args, fmt); + vsnprintf(msg, max_msg_length, fmt, args); + va_end(args); const auto repeatedSummary = [&] - { - Logger::T_LOG_MESSAGE repMsg = RepeatMessage.localData(); - repMsg.message = "Previous line repeats " + QString::number(RepeatCount.localData()) + " times"; - repMsg.utime = QDateTime::currentMSecsSinceEpoch(); + { + Logger::T_LOG_MESSAGE repMsg = RepeatMessage.localData(); + repMsg.message = "Previous line repeats " + QString::number(RepeatCount.localData()) + " times"; + repMsg.utime = QDateTime::currentMSecsSinceEpoch(); - write(repMsg); + write(repMsg); #ifndef _WIN32 - if ( _syslogEnabled && repMsg.level >= Logger::WARNING ) - { - syslog (LogLevelSysLog[repMsg.level], "Previous line repeats %d times", RepeatCount.localData()); - } + if (_syslogEnabled && repMsg.level >= LOG_WARNING) + { + syslog(LogLevelSysLog[repMsg.level], "Previous line repeats %d times", RepeatCount.localData()); + } #endif - RepeatCount.setLocalData(0); - }; + RepeatCount.setLocalData(0); + }; - if (RepeatMessage.localData().loggerName == _name && - RepeatMessage.localData().loggerSubName == _subName && + if (RepeatMessage.localData().loggerName == _name && + RepeatMessage.localData().loggerSubName == _subName && RepeatMessage.localData().function == func && - RepeatMessage.localData().message == msg && + RepeatMessage.localData().message == msg && RepeatMessage.localData().line == line) { if (RepeatCount.localData() >= MaxRepeatCountSize) @@ -214,28 +216,28 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u } else { - if (RepeatCount.localData()) + if (RepeatCount.localData() != 0) { repeatedSummary(); } Logger::T_LOG_MESSAGE logMsg; - logMsg.loggerName = _name; - logMsg.loggerSubName = _subName; - logMsg.function = QString(func); - logMsg.line = line; - logMsg.fileName = FileUtils::getBaseName(sourceFile); - logMsg.utime = QDateTime::currentMSecsSinceEpoch(); - logMsg.message = QString(msg); - logMsg.level = level; + logMsg.loggerName = _name; + logMsg.loggerSubName = _subName; + logMsg.function = QString(func); + logMsg.line = line; + logMsg.fileName = FileUtils::getBaseName(sourceFile); + logMsg.utime = QDateTime::currentMSecsSinceEpoch(); + logMsg.message = QString(msg); + logMsg.level = level; logMsg.levelString = LogLevelStrings[level]; write(logMsg); #ifndef _WIN32 - if ( _syslogEnabled && level >= Logger::WARNING ) + if (_syslogEnabled && level >= LOG_WARNING) { - syslog (LogLevelSysLog[level], "%s", msg); + syslog(LogLevelSysLog[level], "%s", msg); } #endif RepeatMessage.setLocalData(logMsg); @@ -245,8 +247,7 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u QScopedPointer LoggerManager::instance; LoggerManager::LoggerManager() - : QObject() - , _loggerMaxMsgBufferSize(MAX_LOG_MSG_BUFFERED) + : _loggerMaxMsgBufferSize(MAX_LOG_MSG_BUFFERED) { _logMessageBuffer.reserve(_loggerMaxMsgBufferSize); } @@ -263,7 +264,7 @@ QJsonArray LoggerManager::getLogMessageBuffer(Logger::LogLevel filter) const { QJsonArray messageArray; { - for (const auto &logLine : std::as_const(_logMessageBuffer)) + for (const auto& logLine : std::as_const(_logMessageBuffer)) { if (logLine.level >= filter) { @@ -284,7 +285,7 @@ QJsonArray LoggerManager::getLogMessageBuffer(Logger::LogLevel filter) const return messageArray; } -void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE & msg) +void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE& msg) { _logMessageBuffer.push_back(msg); if (_logMessageBuffer.length() > _loggerMaxMsgBufferSize) diff --git a/libsrc/utils/NetOrigin.cpp b/libsrc/utils/NetOrigin.cpp index f85589bd3..686ce1e1e 100644 --- a/libsrc/utils/NetOrigin.cpp +++ b/libsrc/utils/NetOrigin.cpp @@ -8,46 +8,10 @@ NetOrigin* NetOrigin::instance = nullptr; NetOrigin::NetOrigin(QObject* parent, Logger* log) : QObject(parent) , _log(log) - , _isInternetAccessAllowed(false) - , _isInternetAccessRestricted(false) - , _ipWhitelist() { NetOrigin::instance = this; } -bool NetOrigin::accessAllowed(const QHostAddress& address, const QHostAddress& local) const -{ - bool isAllowed {false}; - - if(isLocalAddress(address, local)) - { - isAllowed = true; - } - else - { - if(_isInternetAccessAllowed) - { - if (!_isInternetAccessRestricted) - { - isAllowed = true; - } - else - { - for (const QHostAddress &listAddress : _ipWhitelist) - { - if (address.isEqual(listAddress)) - { - isAllowed = true; - break; - } - } - WarningIf(!isAllowed, _log,"Client connection from IP address '%s' has been rejected! It's not whitelisted.",QSTRING_CSTR(address.toString())); - } - } - } - return isAllowed; -} - bool NetOrigin::isLocalAddress(const QHostAddress& ipAddress, const QHostAddress& /*local*/) const { @@ -89,32 +53,3 @@ bool NetOrigin::isLocalAddress(const QHostAddress& ipAddress, const QHostAddress return false; } -void NetOrigin::handleSettingsUpdate(settings::type type, const QJsonDocument& config) -{ - if(type == settings::NETWORK) - { - const QJsonObject& obj = config.object(); - _isInternetAccessAllowed = obj["internetAccessAPI"].toBool(false); - _isInternetAccessRestricted = obj["restirctedInternetAccessAPI"].toBool(false); - const QJsonArray arr = obj["ipWhitelist"].toArray(); - - _ipWhitelist.clear(); - - for(const auto& item : std::as_const(arr)) - { - const QString& entry = item.toString(""); - if(entry.isEmpty()) - { - continue; - } - - QHostAddress host(entry); - if(host.isNull()) - { - Warning(_log,"The whitelisted IP address '%s' isn't valid! Skipped",QSTRING_CSTR(entry)); - continue; - } - _ipWhitelist << host; - } - } -} diff --git a/libsrc/webserver/QtHttpServer.cpp b/libsrc/webserver/QtHttpServer.cpp index 2f61882a9..247c79311 100644 --- a/libsrc/webserver/QtHttpServer.cpp +++ b/libsrc/webserver/QtHttpServer.cpp @@ -84,33 +84,26 @@ void QtHttpServer::onClientConnected (void) { if (QTcpSocket * sock = m_sockServer->nextPendingConnection ()) { - 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 (m_useSsl) + { + if (QSslSocket* ssl = qobject_cast (sock)) { - if (QSslSocket* ssl = qobject_cast (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); - emit clientConnected (wrapper->getGuid ()); - } - else - { - sock->close(); } + + QtHttpClientWrapper* wrapper = new QtHttpClientWrapper(sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this); + m_socksClientsHash.insert(sock, wrapper); + emit clientConnected (wrapper->getGuid ()); } } } diff --git a/libsrc/webserver/WebServer.cpp b/libsrc/webserver/WebServer.cpp index daf9063d5..c84f92536 100644 --- a/libsrc/webserver/WebServer.cpp +++ b/libsrc/webserver/WebServer.cpp @@ -67,7 +67,7 @@ void WebServer::onServerStarted(quint16 port) { _inited = true; - Info(_log, "'%s' started on port %d", _server->getServerName().toStdString().c_str(), port); + Info(_log, "%s started on port %d", _server->getServerName().toStdString().c_str(), port); if (_useSsl) { diff --git a/settings/hyperion.settings.json.default b/settings/hyperion.settings.json.default index b059db23b..3959534c8 100644 --- a/settings/hyperion.settings.json.default +++ b/settings/hyperion.settings.json.default @@ -113,9 +113,6 @@ "level":"warn" }, "network":{ - "internetAccessAPI":false, - "restirctedInternetAccessAPI":false, - "ipWhitelist":[], "localApiAuth":false }, "osEvents":{ diff --git a/src/hyperion-aml/hyperion-aml.cpp b/src/hyperion-aml/hyperion-aml.cpp index 4a8670c51..0c02e2eb8 100644 --- a/src/hyperion-aml/hyperion-aml.cpp +++ b/src/hyperion-aml/hyperion-aml.cpp @@ -46,7 +46,7 @@ int main(int argc, char ** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -60,6 +60,10 @@ int main(int argc, char ** argv) QTimer::singleShot(0, [&app]() { app.quit(); }); }); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); + QLocale::setDefault(QLocale::c()); + // create the option parser and initialize all parser Parser parser( CAPTURE_TYPE + " capture application for Hyperion. Will automatically search a Hyperion server if -a option is not used. Please note that if you have more than one server running it's more or less random which one will be used."); @@ -88,7 +92,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-dispmanx/hyperion-dispmanx.cpp b/src/hyperion-dispmanx/hyperion-dispmanx.cpp index 4901ec85d..639432faf 100644 --- a/src/hyperion-dispmanx/hyperion-dispmanx.cpp +++ b/src/hyperion-dispmanx/hyperion-dispmanx.cpp @@ -48,7 +48,7 @@ int main(int argc, char ** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -62,6 +62,10 @@ int main(int argc, char ** argv) QTimer::singleShot(0, [&app]() { app.quit(); }); }); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); + QLocale::setDefault(QLocale::c()); + // create the option parser and initialize all parameters Parser parser( CAPTURE_TYPE + " capture application for Hyperion. Will automatically search a Hyperion server if -a option is not used. Please note that if you have more than one server running it's more or less random which one will be used."); @@ -90,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } diff --git a/src/hyperion-framebuffer/hyperion-framebuffer.cpp b/src/hyperion-framebuffer/hyperion-framebuffer.cpp index 3dfa744b4..efc2290e3 100644 --- a/src/hyperion-framebuffer/hyperion-framebuffer.cpp +++ b/src/hyperion-framebuffer/hyperion-framebuffer.cpp @@ -47,7 +47,7 @@ int main(int argc, char ** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -61,6 +61,10 @@ int main(int argc, char ** argv) QTimer::singleShot(0, [&app]() { app.quit(); }); }); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); + QLocale::setDefault(QLocale::c()); + // create the option parser and initialize all parameters Parser parser( CAPTURE_TYPE + " capture application for Hyperion. Will automatically search a Hyperion server if -a option is not used. Please note that if you have more than one server running it's more or less random which one will be used."); @@ -91,7 +95,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-osx/hyperion-osx.cpp b/src/hyperion-osx/hyperion-osx.cpp index 57ebf7fad..24fc9a829 100644 --- a/src/hyperion-osx/hyperion-osx.cpp +++ b/src/hyperion-osx/hyperion-osx.cpp @@ -47,7 +47,7 @@ int main(int argc, char ** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -61,6 +61,10 @@ int main(int argc, char ** argv) QTimer::singleShot(0, [&app]() { app.quit(); }); }); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); + QLocale::setDefault(QLocale::c()); + // create the option parser and initialize all parameters Parser parser( CAPTURE_TYPE + " capture application for Hyperion. Will automatically search a Hyperion server if -a option is not used. Please note that if you have more than one server running it's more or less random which one will be used."); @@ -91,7 +95,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-qt/hyperion-qt.cpp b/src/hyperion-qt/hyperion-qt.cpp index 326d3ae11..01d7bc2fb 100644 --- a/src/hyperion-qt/hyperion-qt.cpp +++ b/src/hyperion-qt/hyperion-qt.cpp @@ -46,7 +46,7 @@ int main(int argc, char ** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QGuiApplication const app(argc, argv); @@ -60,6 +60,10 @@ int main(int argc, char ** argv) QTimer::singleShot(0, [&app]() { app.quit(); }); }); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); + QLocale::setDefault(QLocale::c()); + // create the option parser and initialize all parameters Parser parser( CAPTURE_TYPE + " capture application for Hyperion. Will automatically search a Hyperion server if -a option is not used. Please note that if you have more than one server running it's more or less random which one will be used."); @@ -90,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index a3bdaca9f..963e5732b 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -83,7 +83,7 @@ int main(int argc, char * argv[]) DefaultSignalHandler::install(); Logger* log = Logger::getInstance("REMOTE"); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -97,8 +97,8 @@ int main(int argc, char * argv[]) QTimer::singleShot(0, [&app]() { app.quit(); }); }); - // force the locale - setlocale(LC_ALL, "C"); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); QLocale::setDefault(QLocale::c()); // create the option parser and initialize all parameters @@ -167,7 +167,7 @@ int main(int argc, char * argv[]) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index 656c692b4..3f38b906e 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -50,7 +50,7 @@ int main(int argc, char** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -64,8 +64,8 @@ int main(int argc, char** argv) QTimer::singleShot(0, [&app]() { app.quit(); }); }); - // force the locale - setlocale(LC_ALL, "C"); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); QLocale::setDefault(QLocale::c()); // register the image type to use in signals @@ -137,7 +137,7 @@ int main(int argc, char** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-x11/hyperion-x11.cpp b/src/hyperion-x11/hyperion-x11.cpp index 0a5faf702..62867ba87 100644 --- a/src/hyperion-x11/hyperion-x11.cpp +++ b/src/hyperion-x11/hyperion-x11.cpp @@ -46,7 +46,7 @@ int main(int argc, char ** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -60,6 +60,10 @@ int main(int argc, char ** argv) QTimer::singleShot(0, [&app]() { app.quit(); }); }); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); + QLocale::setDefault(QLocale::c()); + // create the option parser and initialize all parameters Parser parser( CAPTURE_TYPE + " capture application for Hyperion. Will automatically search a Hyperion server if -a option is not used. Please note that if you have more than one server running it's more or less random which one will be used."); @@ -90,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperion-xcb/hyperion-xcb.cpp b/src/hyperion-xcb/hyperion-xcb.cpp index ecc0b4f0f..588263d26 100644 --- a/src/hyperion-xcb/hyperion-xcb.cpp +++ b/src/hyperion-xcb/hyperion-xcb.cpp @@ -46,7 +46,7 @@ int main(int argc, char ** argv) ErrorManager errorManager; Logger *log = Logger::getInstance(CAPTURE_TYPE.toUpper()); - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); QCoreApplication const app(argc, argv); @@ -59,6 +59,11 @@ int main(int argc, char ** argv) Error(log, "Error occured: %s", QSTRING_CSTR(error)); QTimer::singleShot(0, [&app]() { app.quit(); }); }); + + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); + QLocale::setDefault(QLocale::c()); + // create the option parser and initialize all parameters Parser parser( CAPTURE_TYPE + " capture application for Hyperion. Will automatically search a Hyperion server if -a option is not used. Please note that if you have more than one server running it's more or less random which one will be used."); @@ -89,7 +94,7 @@ int main(int argc, char ** argv) // check if debug logging is required if (parser.isSet(argDebug)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } // check if we need to display the usage. exit if we do. diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 006db88e1..0ed8ffeda 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -68,9 +68,6 @@ // InstanceManager Hyperion #include -// NetOrigin checks -#include - #if defined(ENABLE_EFFECTENGINE) // Init Python #include @@ -97,7 +94,6 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo , _pyInit(new PythonInit()) #endif , _authManager(new AuthManager(this)) - , _netOrigin(new NetOrigin(this)) , _currVideoMode(VideoMode::VIDEO_2D) { HyperionDaemon::daemon = this; @@ -250,10 +246,6 @@ void HyperionDaemon::createNetworkServices() connect(this, &HyperionDaemon::settingsChanged, _authManager.get(), &AuthManager::handleSettingsUpdate); _authManager->handleSettingsUpdate(settings::NETWORK, _settingsManager->getSetting(settings::NETWORK)); - // connect and apply settings for NetOrigin - connect(this, &HyperionDaemon::settingsChanged, _netOrigin.get(), &NetOrigin::handleSettingsUpdate); - _netOrigin->handleSettingsUpdate(settings::NETWORK, _settingsManager->getSetting(settings::NETWORK)); - #ifdef ENABLE_MDNS // Create mDNS-Provider and mDNS-Browser in own thread _mDnsThread.reset(new QThread()); @@ -495,7 +487,6 @@ void HyperionDaemon::startEventServices() _cecHandler.reset(new CECHandler(getSetting(settings::CECEVENTS))); _cecHandler->moveToThread(_cecHandlerThread.get()); connect(_cecHandlerThread.get(), &QThread::started, _cecHandler.get(), &CECHandler::start); - connect(_cecHandlerThread.get(), &QThread::finished, _cecHandler.get(), &CECHandler::stop); connect(this, &HyperionDaemon::settingsChanged, _cecHandler.get(), &CECHandler::handleSettingsUpdate); Info(_log, "CEC event handler created"); _cecHandlerThread->start(); @@ -507,15 +498,14 @@ void HyperionDaemon::startEventServices() void HyperionDaemon::stopEventServices() { #if defined(ENABLE_CEC) - if (_cecHandlerThread != nullptr) + QMetaObject::invokeMethod(_cecHandler.get(), &CECHandler::stop, Qt::QueuedConnection); + if (_cecHandlerThread->isRunning()) { - if (_cecHandlerThread->isRunning()) - { - _cecHandlerThread->quit(); - _cecHandlerThread->wait(); - } + _cecHandlerThread->quit(); + _cecHandlerThread->wait(); } #endif + _osEventHandler.reset(nullptr); _eventScheduler.reset(nullptr); } @@ -548,19 +538,19 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs std::string const level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug if (level == "silent") { - Logger::setLogLevel(Logger::OFF); + Logger::setLogLevel(Logger::LOG_OFF); } else if (level == "warn") { - Logger::setLogLevel(Logger::LogLevel::WARNING); + Logger::setLogLevel(Logger::LOG_WARNING); } else if (level == "verbose") { - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); } else if (level == "debug") { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); } } diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index 8a8b8f2f8..6e9f14b30 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -107,7 +107,6 @@ class ProtoServer; class MessageForwarder; #endif class AuthManager; -class NetOrigin; class CECHandler; class HyperionDaemon : public QObject @@ -258,7 +257,6 @@ private slots: /// Network services QScopedPointer _authManager; - QScopedPointer _netOrigin; QScopedPointer _jsonServer; QScopedPointer _jsonServerThread; QScopedPointer _webServer; @@ -289,7 +287,7 @@ private slots: QScopedPointer _osEventHandler; QScopedPointer _eventScheduler; #ifdef ENABLE_CEC - QScopedPointer _cecHandler; + QScopedPointer _cecHandler; QScopedPointer _cecHandlerThread; #endif diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index 304efad6e..28538c20a 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -117,7 +117,7 @@ int main(int argc, char** argv) // initialize main logger and set global log level Logger* log = Logger::getInstance("MAIN"); - Logger::setLogLevel(Logger::WARNING); + Logger::setLogLevel(Logger::LOG_WARNING); // Initialising QCoreApplication QScopedPointer app(createApplication(argc, argv)); @@ -142,8 +142,8 @@ int main(int argc, char** argv) app->setApplicationName(APPLICATION_NAME); app->setApplicationVersion(QString("%1 (%2)").arg(HYPERION_VERSION, HYPERION_BUILD_ID)); - // force the locale - setlocale(LC_ALL, "C"); + // Force locale to have predictable, minimal behavior while still supporting full Unicode. + setlocale(LC_ALL, "C.UTF-8"); QLocale::setDefault(QLocale::c()); Parser parser(APPLICATION_NAME); @@ -172,7 +172,7 @@ int main(int argc, char** argv) parser.process(*qApp); #ifdef WIN32 - //Attach the output to an existing console if available + //Attach the output to an existing console if available openConsole(false); #endif @@ -216,19 +216,19 @@ int main(int argc, char** argv) int logLevelCheck = 0; if (parser.isSet(silentLogOption)) { - Logger::setLogLevel(Logger::OFF); + Logger::setLogLevel(Logger::LOG_OFF); logLevelCheck++; } if (parser.isSet(infoLogOption)) { - Logger::setLogLevel(Logger::INFO); + Logger::setLogLevel(Logger::LOG_INFO); logLevelCheck++; } if (parser.isSet(debugLogOption)) { - Logger::setLogLevel(Logger::DEBUG); + Logger::setLogLevel(Logger::LOG_DEBUG); logLevelCheck++; } @@ -273,7 +273,7 @@ int main(int argc, char** argv) QFile::remove(destinationFilePath); } - if (Logger::getLogLevel() == Logger::DEBUG) + if (Logger::getLogLevel() == Logger::LOG_DEBUG) { std::cout << "Copy \"" << sourceFilePath.toStdString() << "\" -> \"" << destinationFilePath.toStdString() << "\"" << '\n'; } diff --git a/src/hyperiond/systray.cpp b/src/hyperiond/systray.cpp index 1e8074ff0..c02b9c9f2 100644 --- a/src/hyperiond/systray.cpp +++ b/src/hyperiond/systray.cpp @@ -9,6 +9,7 @@ #include #include // Required to determine the cmake options +#include #if defined(ENABLE_EFFECTENGINE) #include #include @@ -124,7 +125,7 @@ QAction *SysTray::createAction(const QString &text, const QString &iconPath, con void SysTray::setColor(int instance, const QColor &color) { - QSharedPointer hyperion = HyperionIManager::getInstance()->getHyperionInstance(instance); + QSharedPointer const hyperion = HyperionIManager::getInstance()->getHyperionInstance(instance); if (!hyperion.isNull()) { std::vector rgbColor {color.rgb()}; @@ -135,7 +136,7 @@ void SysTray::setColor(int instance, const QColor &color) #if defined(ENABLE_EFFECTENGINE) void SysTray::setEffect(int instance, const QString& effectName) { - QSharedPointer hyperion = HyperionIManager::getInstance()->getHyperionInstance(instance); + QSharedPointer const hyperion = HyperionIManager::getInstance()->getHyperionInstance(instance); if (!hyperion.isNull()) { emit hyperion->setEffect(effectName, PriorityMuxer::FG_PRIORITY, PriorityMuxer::ENDLESS); @@ -161,7 +162,7 @@ void SysTray::showColorDialog(int instance) void SysTray::clearSource(int instance) { - QSharedPointer hyperion = HyperionIManager::getInstance()->getHyperionInstance(instance); + QSharedPointer const hyperion = HyperionIManager::getInstance()->getHyperionInstance(instance); if (!hyperion.isNull()) { emit hyperion->clear(PriorityMuxer::FG_PRIORITY); diff --git a/src/hyperiond/systray.h b/src/hyperiond/systray.h index c53d1afaf..77b55ed29 100644 --- a/src/hyperiond/systray.h +++ b/src/hyperiond/systray.h @@ -12,7 +12,6 @@ #include #include -#include #include #include