diff --git a/.ci/ci_build.sh b/.ci/ci_build.sh index d74d4144..1eda6dc0 100755 --- a/.ci/ci_build.sh +++ b/.ci/ci_build.sh @@ -35,7 +35,7 @@ elif [[ $CI_NAME == *"mingw64_nt"* || "$CI_NAME" == 'windows_nt' ]]; then echo "Number of Cores $NUMBER_OF_PROCESSORS" mkdir build || exit 1 cd build - cmake -G "Visual Studio 16 2019" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../ || exit 2 + cmake -G "Visual Studio 17 2022" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../ || exit 2 cmake --build . --target package --config Release -- -nologo -v:m -maxcpucount || exit 3 exit 0; exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index c3b18649..5e94c292 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -121,10 +121,10 @@ jobs: windows: name: Windows - runs-on: windows-latest + runs-on: windows-2022 env: - VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC' - QT_VERSION: 5.15.0 + VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC' + QT_VERSION: 5.15.2 steps: - name: Checkout uses: actions/checkout@v1 @@ -159,12 +159,6 @@ jobs: path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey key: ${{ runner.os }}-chocolatey - - name: "Remove Redistributable" - shell: cmd - run: | - MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5} - MsiExec.exe /passive /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7} - - name: Install Python, NSIS, OpenSSL, DirectX SDK shell: powershell run: | diff --git a/.github/workflows/push-master.yml b/.github/workflows/push-master.yml index 21328f13..e332bf9d 100644 --- a/.github/workflows/push-master.yml +++ b/.github/workflows/push-master.yml @@ -91,10 +91,10 @@ jobs: windows: name: Windows - runs-on: windows-latest + runs-on: windows-2022 env: - VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC' - QT_VERSION: 5.15.0 + VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC' + QT_VERSION: 5.15.2 steps: - name: Checkout uses: actions/checkout@v1 @@ -122,12 +122,6 @@ jobs: path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey key: ${{ runner.os }}-chocolatey - - name: "Remove Redistributable" - shell: cmd - run: | - MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5} - MsiExec.exe /passive /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7} - - name: Install Python, NSIS, OpenSSL, DirectX SDK shell: powershell run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 5524123e..7e77eed7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,18 +12,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow to build a "light" version of Hyperion, i.e. no grabbers, or services like flat-/proto buffers, boblight, CEC - Allow to restart Hyperion via Systray +- LED Matrix Layout - Support vertical cabling direction (#1420) +- Support additional Yeelight models +- LED-Devices: Show warning, if get properties failed (Network devices: indication that network device is not reachable) +- hyperion-remote: Show image filename in UI for images sent ### Changed - Colors Smoothing is started in pause mode to save resources, when Hyperion starts with no active source +- Boblight: Support multiple Boblight clients with different priorities +- UI: Allow configuration of a Boblight server per LED-instance +- UI: LED Layout - Removed limitations on indention ### Fixed - Effects: Fix image URL in Matrix effect +- Effects: Fix that start effect is stuck on UI - Fixes that the Led-Device output flow was interrupted, by an enabling API request on an already enabled device (#967 - Yeelight - Workaround: Ignore error when setting music mode = off, but the music-mode is already off (#1372) - Standalone grabbers: Improved fps help/error text, fixed default address and port, fixed auto discovery of Hyperion server in hyperion-remote - Fixed Qt version override, e.g. set via QTDIR +- Remote control UI: Treat duration=0 as endless +- Stop Web-Browser capture when user triggers other activities +- Treat http headers case insensitive (RFC 2616) +- Qt-Grabber: Fixed position handling of multiple monitors (#1320, #1403) +- Fixed: Signal detection does not switch off all instances (#1281) +- Reworked PriorityMuxer and Sub-scriptions ## Removed diff --git a/assets/webconfig/content/conf_instcapture.html b/assets/webconfig/content/conf_instcapture.html index 73a8bf75..20ab1075 100644 --- a/assets/webconfig/content/conf_instcapture.html +++ b/assets/webconfig/content/conf_instcapture.html @@ -2,7 +2,7 @@
- +
diff --git a/assets/webconfig/content/conf_leds.html b/assets/webconfig/content/conf_leds.html index 240b06fc..f0545383 100755 --- a/assets/webconfig/content/conf_leds.html +++ b/assets/webconfig/content/conf_leds.html @@ -199,14 +199,14 @@ - +
%h
- +
%v
@@ -215,14 +215,14 @@ - +
%
- +
%
@@ -231,14 +231,14 @@ - +
%
- +
%
@@ -247,14 +247,14 @@ - +
%
- +
%
@@ -402,3 +402,4 @@ + diff --git a/assets/webconfig/content/dashboard.html b/assets/webconfig/content/dashboard.html index 133f5dff..77b3e950 100644 --- a/assets/webconfig/content/dashboard.html +++ b/assets/webconfig/content/dashboard.html @@ -70,6 +70,14 @@ + + + proto + + unknown + + + json diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index ff707da7..6e394e38 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -55,6 +55,8 @@ "conf_leds_contr_label_contrtype": "Controller type:", "conf_leds_device_info_log": "In case your LEDs do not work, check here for errors:", "conf_leds_device_intro": "Hyperion supports a lot of controllers to transmit data to your target device. Select a LED controller out of the sorted list and configure it. We have chosen the best default settings for each device.", + "conf_leds_error_get_properties_text" : "Failed to get the device's properties. Please check the configuration items.", + "conf_leds_error_get_properties_title" : "Device properties", "conf_leds_error_hwled_gt_layout": "The hardware LED count ($1) is greater than LEDs configured via layout ($2),
$3 {{plural:$3|LED|LEDs}} will stay black if you continue.", "conf_leds_error_hwled_lt_layout": "The hardware LED count ($1) is less than LEDs configured via layout ($2).
The number of LEDs configured in the layout must not exceed the available LEDs", "conf_leds_error_hwled_gt_maxled": "The hardware LED count ($1) is greater than the maximum number of LEDs supported by the device ($2).
The hardware LED count is set to ($3).", @@ -99,6 +101,7 @@ "conf_leds_layout_generatedconf": "Generated/Current LED Configuration", "conf_leds_layout_intro": "You also need an LED layout, which reflects your LED positions. The classic layout is the usually used TV frame, but we also support LED matrix (LED walls) creation. The view on this layout is ALWAYS from the FRONT of your TV.", "conf_leds_layout_ma_cabling": "Cabling", + "conf_leds_layout_ma_direction": "Direction", "conf_leds_layout_ma_horiz": "Horizontal", "conf_leds_layout_ma_optbottomleft": "Bottom left", "conf_leds_layout_ma_optbottomright": "Bottom right", @@ -185,6 +188,7 @@ "dashboard_infobox_label_instance": "Instance:", "dashboard_infobox_label_latesthyp": "Latest Hyperion version:", "dashboard_infobox_label_platform": "Platform:", + "dashboard_infobox_label_port_boblight": "Boblight Server:", "dashboard_infobox_label_port_flat": "Flatbuffer:", "dashboard_infobox_label_port_json": "JSON-Server:", "dashboard_infobox_label_port_proto": "Protobuffer:", diff --git a/assets/webconfig/js/content_dashboard.js b/assets/webconfig/js/content_dashboard.js index 2a547a9b..087df901 100644 --- a/assets/webconfig/js/content_dashboard.js +++ b/assets/webconfig/js/content_dashboard.js @@ -137,7 +137,14 @@ $(document).ready(function () { } else { $("#dash_ports_proto_row").hide(); } - + + if (jQuery.inArray("boblight", window.serverInfo.services) !== -1) { + var boblightPort = window.serverConfig.boblightServer.enable ? window.serverConfig.boblightServer.port : $.i18n('general_disabled'); + $('#dash_boblightPort').html(boblightPort); + } else { + $("#dash_ports_boblight_row").hide(); + } + var jsonPort = window.serverConfig.jsonServer.port; $('#dash_jsonPort').html(jsonPort); var wsPorts = window.serverConfig.webConfig.port + ' | ' + window.serverConfig.webConfig.sslPort; diff --git a/assets/webconfig/js/content_index.js b/assets/webconfig/js/content_index.js index 5a63f4ad..f444a83a 100644 --- a/assets/webconfig/js/content_index.js +++ b/assets/webconfig/js/content_index.js @@ -226,7 +226,9 @@ $(document).ready(function () { //Hide capture menu entries, if no grabbers are available if ((window.serverInfo.grabbers.screen.available.length === 0) && (window.serverInfo.grabbers.video.available.length === 0)) { $("#MenuItemGrabber").attr('style', 'display:none') - $("#MenuItemInstCapture").attr('style', 'display:none') + if ((jQuery.inArray("boblight", window.serverInfo.services) === -1)) { + $("#MenuItemInstCapture").attr('style', 'display:none') + } } //Hide effectsconfigurator menu entry, if effectengine is not available diff --git a/assets/webconfig/js/content_instcapture.js b/assets/webconfig/js/content_instcapture.js index d4e89030..030a68c9 100644 --- a/assets/webconfig/js/content_instcapture.js +++ b/assets/webconfig/js/content_instcapture.js @@ -4,113 +4,184 @@ $(document).ready(function () { var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0); var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0); + var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1); + // update instance listing updateHyperionInstanceListing(); var conf_editor_instCapt = null; + var conf_editor_bobl = null; // Instance Capture - $('#conf_cont').append(createRow('conf_cont_instCapt')); - $('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', '')); + if (window.showOptHelp) { - $('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title"))); + if (screenGrabberAvailable || videoGrabberAvailable) { + $('#conf_cont').append(createRow('conf_cont_instCapt')); + $('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', '')); + $('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title"))); + } + //boblight + if (BOBLIGHT_ENABLED) { + $('#conf_cont').append(createRow('conf_cont_bobl')); + $('#conf_cont_bobl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver', '')); + $('#conf_cont_bobl').append(createHelpTable(window.schema.boblightServer.properties, $.i18n("edt_conf_bobls_heading_title"), "boblightServerHelpPanelId")); + } + } + else { + $('#conf_cont').addClass('row'); + if (screenGrabberAvailable || videoGrabberAvailable) { + $('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', '')); + } + if (BOBLIGHT_ENABLED) { + $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver', '')); + } } - // Instance Capture - conf_editor_instCapt = createJsonEditor('editor_container_instCapt', { - instCapture: window.schema.instCapture - }, true, true); + if (screenGrabberAvailable || videoGrabberAvailable) { - var grabber_config_info_html = '

' + $.i18n('dashboard_infobox_label_title') + '

'; - grabber_config_info_html += '' + $.i18n('conf_grabber_inst_grabber_config_info') + ''; - grabber_config_info_html += ''; - grabber_config_info_html += '
'; - $('#editor_container_instCapt').append(grabber_config_info_html); + // Instance Capture + conf_editor_instCapt = createJsonEditor('editor_container_instCapt', { + instCapture: window.schema.instCapture + }, true, true); - conf_editor_instCapt.on('ready', function () { + var grabber_config_info_html = '

' + $.i18n('dashboard_infobox_label_title') + '

'; + grabber_config_info_html += '' + $.i18n('conf_grabber_inst_grabber_config_info') + ''; + grabber_config_info_html += ''; + grabber_config_info_html += '
'; + $('#editor_container_instCapt').append(grabber_config_info_html); - if (screenGrabberAvailable) { - if (!window.serverConfig.framegrabber.enable) { - conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(false); - conf_editor_instCapt.getEditor("root.instCapture.systemEnable").disable(); - } - else { - conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(window.serverConfig.instCapture.systemEnable); - } - } else { - showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemEnable", false); - showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemGrabberDevice", false); - showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemPriority", false); - } + conf_editor_instCapt.on('ready', function () { - if (videoGrabberAvailable) { - if (!window.serverConfig.grabberV4L2.enable) { - conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(false); - conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable(); - } - else { - conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(window.serverConfig.instCapture.v4lEnable); - - } - } else { - showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lGrabberDevice", false); - showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lEnable", false); - showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lPriority", false); - } - - }); - - conf_editor_instCapt.on('change', function () { - - if (!conf_editor_instCapt.validate().length) { - if (!window.serverConfig.framegrabber.enable && !window.serverConfig.grabberV4L2.enable) { - $('#btn_submit_instCapt').attr('disabled', true); + if (screenGrabberAvailable) { + if (!window.serverConfig.framegrabber.enable) { + conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(false); + conf_editor_instCapt.getEditor("root.instCapture.systemEnable").disable(); + } + else { + conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(window.serverConfig.instCapture.systemEnable); + } } else { - window.readOnlyMode ? $('#btn_submit_instCapt').attr('disabled', true) : $('#btn_submit_instCapt').attr('disabled', false); + showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemEnable", false); + showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemGrabberDevice", false); + showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemPriority", false); } - } - else { - $('#btn_submit_instCapt').attr('disabled', true); - } - }); - conf_editor_instCapt.watch('root.instCapture.systemEnable', () => { + if (videoGrabberAvailable) { + if (!window.serverConfig.grabberV4L2.enable) { + conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(false); + conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable(); + } + else { + conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(window.serverConfig.instCapture.v4lEnable); - var screenEnable = conf_editor_instCapt.getEditor("root.instCapture.systemEnable").getValue(); - if (screenEnable) { - conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").setValue(window.serverConfig.framegrabber.available_devices); - conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").disable(); - showInputOptions("instCapture", ["systemGrabberDevice"], true); - showInputOptions("instCapture", ["systemPriority"], true); - - } else { - showInputOptions("instCapture", ["systemGrabberDevice"], false); - showInputOptions("instCapture", ["systemPriority"], false); - } - - }); - - conf_editor_instCapt.watch('root.instCapture.v4lEnable', () => { - console.log("instCapt.watch(root.instCapture.v4lEnable"); - var videoEnable = conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").getValue(); - if (videoEnable) { - conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").setValue(window.serverConfig.grabberV4L2.available_devices); - conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").disable(); - showInputOptions("instCapture", ["v4lGrabberDevice"], true); - showInputOptions("instCapture", ["v4lPriority"], true); - } - else { - if (!window.serverConfig.grabberV4L2.enable) { - conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable(); + } + } else { + showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lGrabberDevice", false); + showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lEnable", false); + showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lPriority", false); } - showInputOptions("instCapture", ["v4lGrabberDevice"], false); - showInputOptions("instCapture", ["v4lPriority"], false); - } - }); - $('#btn_submit_instCapt').off().on('click', function () { - requestWriteConfig(conf_editor_instCapt.getValue()); - }); + }); + + conf_editor_instCapt.on('change', function () { + + if (!conf_editor_instCapt.validate().length) { + if (!window.serverConfig.framegrabber.enable && !window.serverConfig.grabberV4L2.enable) { + $('#btn_submit_instCapt').attr('disabled', true); + } else { + window.readOnlyMode ? $('#btn_submit_instCapt').attr('disabled', true) : $('#btn_submit_instCapt').attr('disabled', false); + } + } + else { + $('#btn_submit_instCapt').attr('disabled', true); + } + }); + + conf_editor_instCapt.watch('root.instCapture.systemEnable', () => { + + var screenEnable = conf_editor_instCapt.getEditor("root.instCapture.systemEnable").getValue(); + if (screenEnable) { + conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").setValue(window.serverConfig.framegrabber.available_devices); + conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").disable(); + showInputOptions("instCapture", ["systemGrabberDevice"], true); + showInputOptions("instCapture", ["systemPriority"], true); + + } else { + showInputOptions("instCapture", ["systemGrabberDevice"], false); + showInputOptions("instCapture", ["systemPriority"], false); + } + + }); + + conf_editor_instCapt.watch('root.instCapture.v4lEnable', () => { + var videoEnable = conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").getValue(); + if (videoEnable) { + conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").setValue(window.serverConfig.grabberV4L2.available_devices); + conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").disable(); + showInputOptions("instCapture", ["v4lGrabberDevice"], true); + showInputOptions("instCapture", ["v4lPriority"], true); + } + else { + if (!window.serverConfig.grabberV4L2.enable) { + conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable(); + } + showInputOptions("instCapture", ["v4lGrabberDevice"], false); + showInputOptions("instCapture", ["v4lPriority"], false); + } + }); + + $('#btn_submit_instCapt').off().on('click', function () { + requestWriteConfig(conf_editor_instCapt.getValue()); + }); + } + + //boblight + if (BOBLIGHT_ENABLED) { + conf_editor_bobl = createJsonEditor('editor_container_boblightserver', { + boblightServer: window.schema.boblightServer + }, true, true); + + conf_editor_bobl.on('ready', function () { + var boblightServerEnable = conf_editor_bobl.getEditor("root.boblightServer.enable").getValue(); + if (!boblightServerEnable) { + showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", false); + $('#boblightServerHelpPanelId').hide(); + } + }); + + conf_editor_bobl.on('change', function () { + conf_editor_bobl.validate().length || window.readOnlyMode ? $('#btn_submit_boblightserver').attr('disabled', true) : $('#btn_submit_boblightserver').attr('disabled', false); + }); + + conf_editor_bobl.watch('root.boblightServer.enable', () => { + var boblightServerEnable = conf_editor_bobl.getEditor("root.boblightServer.enable").getValue(); + if (boblightServerEnable) { + //Make port instance specific, if port is still the default one (avoids overlap of used ports) + var port = conf_editor_bobl.getEditor("root.boblightServer.port").getValue(); + if (port === conf_editor_bobl.schema.properties.boblightServer.properties.port.default) { + port += parseInt(window.currentHyperionInstance); + } + conf_editor_bobl.getEditor("root.boblightServer.port").setValue(port); + + showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", true); + $('#boblightServerHelpPanelId').show(); + } else { + showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", false); + $('#boblightServerHelpPanelId').hide(); + } + }); + + $('#btn_submit_boblightserver').off().on('click', function () { + requestWriteConfig(conf_editor_bobl.getValue()); + }); + } + + //create introduction + if (window.showOptHelp) { + if (BOBLIGHT_ENABLED) { + createHint("intro", $.i18n('conf_network_bobl_intro'), "editor_container_boblightserver"); + } + } removeOverlay(); }); diff --git a/assets/webconfig/js/content_leds.js b/assets/webconfig/js/content_leds.js index d87c8cb0..62c94c4e 100755 --- a/assets/webconfig/js/content_leds.js +++ b/assets/webconfig/js/content_leds.js @@ -277,7 +277,7 @@ function createClassicLeds() { aceEdt.set(finalLedArray); } -function createMatrixLayout(ledshoriz, ledsvert, cabling, start) { +function createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction) { // Big thank you to RanzQ (Juha Rantanen) from Github for this script // https://raw.githubusercontent.com/RanzQ/hyperion-audio-effects/master/matrix-config.js @@ -325,15 +325,30 @@ function createMatrixLayout(ledshoriz, ledsvert, cabling, start) { var x, y - for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) { + if (direction === 'vertical') { for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) { - addLed(x, y) + for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) { + + addLed(x, y) + } + if (!parallel) { + downward = !downward + var tmp = startY + startY = endY + endY = tmp + } } - if (!parallel) { - forward = !forward - var tmp = startX - startX = endX - endX = tmp + } else { + for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) { + for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) { + addLed(x, y) + } + if (!parallel) { + forward = !forward + var tmp = startX + startX = endX + endX = tmp + } } } @@ -348,9 +363,10 @@ function createMatrixLeds() { var ledshoriz = parseInt($("#ip_ma_ledshoriz").val()); var ledsvert = parseInt($("#ip_ma_ledsvert").val()); var cabling = $("#ip_ma_cabling").val(); + var direction = $("#ip_ma_direction").val(); var start = $("#ip_ma_start").val(); - nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start); + nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction); finalLedArray = blackListLeds(nonBlacklistLedArray, ledBlacklist); createLedPreview(finalLedArray, 'matrix'); @@ -467,7 +483,63 @@ $(document).ready(function () { // bind change event to all inputs $('.ledCLconstr').bind("change", function () { - valValue(this.id, this.value, this.min, this.max); + + //Ensure Values are in min/max ranges + if ($(this).val() < $(this).attr('min') * 1) { $(this).val($(this).attr('min')); } + if ($(this).val() > $(this).attr('max') * 1) { $(this).val($(this).attr('max')); } + + //top/bottom and left/right must not overlap + switch (this.id) { + case "ip_cl_ptlh": + var ptrh = parseInt($("#ip_cl_ptrh").val()); + if (this.value > ptrh) { + $(this).val(ptrh); + } + break; + case "ip_cl_ptrh": + var ptlh = parseInt($("#ip_cl_ptlh").val()); + if (this.value < ptlh) { + $(this).val(ptlh); + } + break; + case "ip_cl_pblh": + var pbrh = parseInt($("#ip_cl_pbrh").val()); + if (this.value > pbrh) { + $(this).val(pbrh); + } + break; + case "ip_cl_pbrh": + var pblh = parseInt($("#ip_cl_pblh").val()); + if (this.value < pblh) { + $(this).val(pblh); + } + break; + case "ip_cl_ptlv": + var pblv = parseInt($("#ip_cl_pblv").val()); + if (this.value > pblv) { + $(this).val(pblv); + } + break; + case "ip_cl_pblv": + var ptlv = parseInt($("#ip_cl_ptlv").val()); + if (this.value < ptlv) { + $(this).val(ptlv); + } + break; + case "ip_cl_ptrv": + var pbrv = parseInt($("#ip_cl_pbrv").val()); + if (this.value > pbrv) { + $(this).val(pbrv); + } + break; + case "ip_cl_pbrv": + var ptrv = parseInt($("#ip_cl_ptrv").val()); + if (this.value < ptrv) { + $(this).val(ptrv); + } + + default: + } createClassicLeds(); }); @@ -1591,6 +1663,7 @@ async function getProperties_device(ledType, key, params) { } } else { + showNotification('warning', $.i18n('conf_leds_error_get_properties_text'), $.i18n('conf_leds_error_get_properties_title')) $('#btn_submit_controller').attr('disabled', true); $('#btn_test_controller').attr('disabled', true); } @@ -1691,6 +1764,7 @@ function updateElements(ledType, key) { $("#ip_ma_ledshoriz").val(ledProperties.maxColumn); $("#ip_ma_ledsvert").val(ledProperties.maxRow); $("#ip_ma_cabling").val("parallel"); + $("#ip_ma_direction").val("horizontal"); $("#ip_ma_start").val("top-left"); createMatrixLeds(); } diff --git a/assets/webconfig/js/content_network.js b/assets/webconfig/js/content_network.js index 0176decf..0ba4a7d8 100644 --- a/assets/webconfig/js/content_network.js +++ b/assets/webconfig/js/content_network.js @@ -1,7 +1,6 @@ $(document).ready(function () { performTranslation(); - var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1); var FORWARDER_ENABLED = (jQuery.inArray("forwarder", window.serverInfo.services) !== -1); var FLATBUF_SERVER_ENABLED = (jQuery.inArray("flatbuffer", window.serverInfo.services) !== -1); var PROTOTBUF_SERVER_ENABLED = (jQuery.inArray("protobuffer", window.serverInfo.services) !== -1); @@ -10,7 +9,6 @@ $(document).ready(function () { var conf_editor_json = null; var conf_editor_proto = null; var conf_editor_fbs = null; - var conf_editor_bobl = null; var conf_editor_forw = null; addJsonEditorHostValidation(); @@ -40,13 +38,6 @@ $(document).ready(function () { $('#conf_cont_proto').append(createHelpTable(window.schema.protoServer.properties, $.i18n("edt_conf_pbs_heading_title"), "protoServerHelpPanelId")); } - //boblight - if (BOBLIGHT_ENABLED) { - $('#conf_cont').append(createRow('conf_cont_bobl')); - $('#conf_cont_bobl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver', 'panel-system')); - $('#conf_cont_bobl').append(createHelpTable(window.schema.boblightServer.properties, $.i18n("edt_conf_bobls_heading_title"), "boblightServerHelpPanelId")); - } - //forwarder if (FORWARDER_ENABLED) { if (storedAccess != 'default') { @@ -66,9 +57,6 @@ $(document).ready(function () { if (PROTOTBUF_SERVER_ENABLED) { $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_pbs_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver')); } - if (BOBLIGHT_ENABLED) { - $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver')); - } if (FORWARDER_ENABLED) { $('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fw_heading_title"), 'editor_container_forwarder', 'btn_submit_forwarder')); } @@ -147,29 +135,6 @@ $(document).ready(function () { }); } - //boblight - if (BOBLIGHT_ENABLED) { - conf_editor_bobl = createJsonEditor('editor_container_boblightserver', { - boblightServer: window.schema.boblightServer - }, true, true); - - conf_editor_bobl.on('change', function () { - var boblightServerEnable = conf_editor_bobl.getEditor("root.boblightServer.enable").getValue(); - if (boblightServerEnable) { - showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", true); - $('#boblightServerHelpPanelId').show(); - } else { - showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", false); - $('#boblightServerHelpPanelId').hide(); - } - conf_editor_bobl.validate().length || window.readOnlyMode ? $('#btn_submit_boblightserver').attr('disabled', true) : $('#btn_submit_boblightserver').attr('disabled', false); - }); - - $('#btn_submit_boblightserver').off().on('click', function () { - requestWriteConfig(conf_editor_bobl.getValue()); - }); - } - //forwarder if (FORWARDER_ENABLED) { if (storedAccess != 'default') { @@ -205,9 +170,6 @@ $(document).ready(function () { if (PROTOTBUF_SERVER_ENABLED) { createHint("intro", $.i18n('conf_network_proto_intro'), "editor_container_protoserver"); } - if (BOBLIGHT_ENABLED) { - createHint("intro", $.i18n('conf_network_bobl_intro'), "editor_container_boblightserver"); - } if (FORWARDER_ENABLED) { createHint("intro", $.i18n('conf_network_forw_intro'), "editor_container_forwarder"); } diff --git a/assets/webconfig/js/content_remote.js b/assets/webconfig/js/content_remote.js index 17e85c89..7d1f6bb4 100644 --- a/assets/webconfig/js/content_remote.js +++ b/assets/webconfig/js/content_remote.js @@ -128,13 +128,13 @@ $(document).ready(function () { switch (compId) { case "EFFECT": - owner = $.i18n('remote_effects_label_effects') + ' ' + owner; + owner = $.i18n('remote_effects_label_effects') + ' ' + owner; break; case "COLOR": owner = $.i18n('remote_color_label_color') + ' ' + '
'; break; case "IMAGE": - owner = $.i18n('remote_effects_label_picture') + ' ' + owner; + owner = $.i18n('remote_effects_label_picture') + (owner !== undefined ? (' ' + owner): ""); break; case "GRABBER": owner = $.i18n('general_comp_GRABBER') + ': (' + owner + ')'; @@ -153,16 +153,19 @@ $(document).ready(function () { break; } - if (duration && compId != "GRABBER" && compId != "FLATBUFSERVER" && compId != "PROTOSERVER") - owner += '
' + $.i18n('remote_input_duration') + ' ' + duration.toFixed(0) + $.i18n('edt_append_s') + ''; + if (!(duration && duration < 0)) + { + if (duration && compId != "GRABBER" && compId != "FLATBUFSERVER" && compId != "PROTOSERVER") + owner += '
' + $.i18n('remote_input_duration') + ' ' + duration.toFixed(0) + $.i18n('edt_append_s') + ''; - var btn = ''; + var btn = ''; - if ((compId == "EFFECT" || compId == "COLOR" || compId == "IMAGE") && priority < 254) - btn += ''; + if ((compId == "EFFECT" || compId == "COLOR" || compId == "IMAGE") && priority < 254) + btn += ''; - if (btn_type != 'default') - $('.sstbody').append(createTableRow([origin, owner, priority, btn], false, true)); + if (btn_type != 'default') + $('.sstbody').append(createTableRow([origin, owner, priority, btn], false, true)); + } } var btn_auto_color = (window.serverInfo.priorities_autoselect ? "btn-success" : "btn-danger"); var btn_auto_state = (window.serverInfo.priorities_autoselect ? "disabled" : "enabled"); @@ -313,6 +316,9 @@ $(document).ready(function () { if (getStorage('rmduration') != null) { $("#remote_duration").val(getStorage('rmduration')); duration = getStorage('rmduration'); + if (duration == 0) { + duration = ENDLESS; + } } createCP('cp2', cpcolor, function (rgbT, hex) { @@ -332,6 +338,9 @@ $(document).ready(function () { $("#remote_duration").off().on("change", function () { duration = valValue(this.id, this.value, this.min, this.max); setStorage('rmduration', duration); + if (duration == 0) { + duration = ENDLESS; + } }); $("#effect_select").off().on("change", function (event) { @@ -349,8 +358,9 @@ $(document).ready(function () { }); $("#remote_input_repimg").off().on("click", function () { - if (lastImgData != "") + if (lastImgData != "") { requestSetImage(lastImgData, duration, lastFileName); + } }); $("#remote_input_img").change(function () { @@ -380,7 +390,6 @@ $(document).ready(function () { // interval updates $(window.hyperion).on('components-updated', function (e, comp) { - //console.log ("components-updated", e, comp); updateComponent(comp); }); diff --git a/assets/webconfig/js/hyperion.js b/assets/webconfig/js/hyperion.js index 2a6de47f..1515dc03 100644 --- a/assets/webconfig/js/hyperion.js +++ b/assets/webconfig/js/hyperion.js @@ -359,21 +359,25 @@ function requestPriorityClear(prio) if(typeof prio !== 'number') prio = window.webPrio; + $(window.hyperion).trigger({type:"stopBrowerScreenCapture"}); sendToHyperion("clear", "", '"priority":'+prio+''); } function requestClearAll() { + $(window.hyperion).trigger({type:"stopBrowerScreenCapture"}); requestPriorityClear(-1) } function requestPlayEffect(effectName, duration) { + $(window.hyperion).trigger({type:"stopBrowerScreenCapture"}); sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'"},"priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"'); } function requestSetColor(r,g,b,duration) { + $(window.hyperion).trigger({type:"stopBrowerScreenCapture"}); sendToHyperion("color", "", '"color":['+r+','+g+','+b+'], "priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"'); } diff --git a/assets/webconfig/js/streamer.js b/assets/webconfig/js/streamer.js index ee457f66..eb6739ac 100644 --- a/assets/webconfig/js/streamer.js +++ b/assets/webconfig/js/streamer.js @@ -1,111 +1,117 @@ -$(document).ready( function() { +$(document).ready(function () { - // check if browser supports streaming - if(window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia){ - $("#btn_streamer").toggle(); - } + // check if browser supports streaming + if (window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia) { + $("#btn_streamer").toggle(); + } - // variables - var streamActive = false; - var screenshotTimer = ""; - var screenshotIntervalTimeMs = 100; - var streamImageHeight = 0; - var streamImageWidth = 0; - const videoElem = document.getElementById("streamvideo"); - const canvasElem = document.getElementById("streamcanvas"); + // variables + var streamActive = false; + var screenshotTimer = ""; + var screenshotIntervalTimeMs = 100; + var streamImageHeight = 0; + var streamImageWidth = 0; + const videoElem = document.getElementById("streamvideo"); + const canvasElem = document.getElementById("streamcanvas"); - // Options for getDisplayMedia() - var displayMediaOptions = { - video: { - cursor: "never", - width: 170, - height: 100, - frameRate: 15 - }, - audio: false - }; + // Options for getDisplayMedia() + var displayMediaOptions = { + video: { + cursor: "never", + width: 170, + height: 100, + frameRate: 15 + }, + audio: false + }; + async function startCapture() { + streamActive = true; - async function startCapture() { - streamActive = true; + try { + var stream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions); + videoElem.srcObject = stream; - try { - var stream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions); - videoElem.srcObject = stream; + // get the active track of the stream + const track = stream.getVideoTracks()[0]; - // get the active track of the stream - const track = stream.getVideoTracks()[0]; + // listen for track ending, fires when user aborts through browser + track.onended = function (event) { + stopCapture(); + }; - // listen for track ending, fires when user aborts through browser - track.onended = function(event) { - stopCapture(); - }; + // wait for video ready + videoElem.addEventListener('loadedmetadata', (e) => { + window.setTimeout(() => ( + onCapabilitiesReady(track.getSettings()) + ), 500); + }); + } catch (err) { + stopCapture(); + console.error("Error: " + err); + } + } - // wait for video ready - videoElem.addEventListener('loadedmetadata', (e) => { - window.setTimeout(() => ( - onCapabilitiesReady(track.getSettings()) - ), 500); - }); - } catch(err) { - stopCapture(); - console.error("Error: " + err); - } - } + function onCapabilitiesReady(settings) { + // extract real width/height + streamImageWidth = settings.width; + streamImageHeight = settings.height; - function onCapabilitiesReady(settings) { - // extract real width/height - streamImageWidth = settings.width; - streamImageHeight = settings.height; + // start screenshotTimer + updateScrTimer(false); - // start screenshotTimer - updateScrTimer(false); + // we are sending + $("#btn_streamer_icon").addClass("text-danger"); + } - // we are sending - $("#btn_streamer_icon").addClass("text-danger"); - } + function stopCapture(evt) { + streamActive = false; + $("#btn_streamer_icon").removeClass("text-danger"); - function stopCapture(evt) { - streamActive = false; - $("#btn_streamer_icon").removeClass("text-danger"); + updateScrTimer(true); + // sometimes it's null on abort + if (videoElem.srcObject) { + let tracks = videoElem.srcObject.getTracks(); - updateScrTimer(true); - // sometimes it's null on abort - if(videoElem.srcObject){ - let tracks = videoElem.srcObject.getTracks(); + tracks.forEach(track => track.stop()); + videoElem.srcObject = null; + } + requestPriorityClear(1); + } - tracks.forEach(track => track.stop()); - videoElem.srcObject = null; - } - } + function takePicture() { + var context = canvasElem.getContext('2d'); + canvasElem.width = streamImageWidth; + canvasElem.height = streamImageHeight; + context.drawImage(videoElem, 0, 0, streamImageWidth, streamImageHeight); - function takePicture(){ - var context = canvasElem.getContext('2d'); - canvasElem.width = streamImageWidth; - canvasElem.height = streamImageHeight; - context.drawImage(videoElem, 0, 0, streamImageWidth, streamImageHeight); + var data = canvasElem.toDataURL('image/png').split(",")[1]; + requestSetImage(data, -1, "Streaming"); + } - var data = canvasElem.toDataURL('image/png').split(",")[1]; - requestSetImage(data, 2, "Streaming"); - } + // start or update screenshot timer + function updateScrTimer(stop) { + clearInterval(screenshotTimer) - // start or update screenshot timer - function updateScrTimer(stop){ - clearInterval(screenshotTimer) + if (stop === false) { + screenshotTimer = setInterval(() => ( + takePicture() + ), screenshotIntervalTimeMs); + } + } - if(stop === false){ - screenshotTimer = setInterval(() => ( - takePicture() - ), screenshotIntervalTimeMs); - } - } + $("#btn_streamer").off().on("click", function (e) { + if (!$("#btn_streamer_icon").hasClass("text-danger") && !streamActive) { + startCapture(); + } else { + stopCapture(); + } + }); - $("#btn_streamer").off().on("click",function(e){ - if(!$("#btn_streamer_icon").hasClass("text-danger") && !streamActive){ - startCapture(); - } else { - stopCapture(); - } - }); + $(window.hyperion).on("stopBrowerScreenCapture", function (event) { + if (streamActive) { + stopCapture(); + } + }); }); diff --git a/assets/webconfig/js/ui_utils.js b/assets/webconfig/js/ui_utils.js index a33f4274..43a19574 100644 --- a/assets/webconfig/js/ui_utils.js +++ b/assets/webconfig/js/ui_utils.js @@ -1221,13 +1221,13 @@ function getSystemInfo() { info += '- Architecture: ' + sys.architecture + '\n'; if (sys.cpuModelName) - info += '- CPU Model: ' + sys.cpuModelName + '\n'; + info += '- CPU Model: ' + sys.cpuModelName + '\n'; if (sys.cpuModelType) - info += '- CPU Type: ' + sys.cpuModelType + '\n'; + info += '- CPU Type: ' + sys.cpuModelType + '\n'; if (sys.cpuRevision) - info += '- CPU Revision: ' + sys.cpuRevision + '\n'; + info += '- CPU Revision: ' + sys.cpuRevision + '\n'; if (sys.cpuHardware) - info += '- CPU Hardware: ' + sys.cpuHardware + '\n'; + info += '- CPU Hardware: ' + sys.cpuHardware + '\n'; info += '- Kernel: ' + sys.kernelType + ' (' + sys.kernelVersion + ' (WS: ' + sys.wordSize + '))\n'; info += '- Root/Admin: ' + sys.isUserAdmin + '\n'; diff --git a/assets/webconfig/js/wizard.js b/assets/webconfig/js/wizard.js index 3fdf9a4d..40d6eb9a 100755 --- a/assets/webconfig/js/wizard.js +++ b/assets/webconfig/js/wizard.js @@ -304,7 +304,7 @@ function performAction() { h += ''; } else - h += '

' + $.i18n('wiz_cc_lettvshowm', "gey_1, grey_2, grey_3, HGradient, VGradient") + '

'; + h += '

' + $.i18n('wiz_cc_lettvshowm', "grey_1, grey_2, grey_3, HGradient, VGradient") + '

'; $('#wiz_cc_desc').html(h); $('#wiz_cc_btn_sp').off().on('click', function () { switchPicture(["VGradient", "grey_1", "grey_2", "grey_3", "HGradient"]); @@ -1534,8 +1534,8 @@ async function discover_yeelight_lights() { function assign_yeelight_lights() { // Model mappings, see https://www.home-assistant.io/integrations/yeelight/ - var models = ['color', 'color1', 'YLDP02YL', 'YLDP02YL', 'color2', 'YLDP06YL', 'color4', 'YLDP13YL', 'stripe', 'YLDD04YL', 'strip1', 'YLDD01YL', 'YLDD02YL']; - + var models = ['color', 'color1', 'YLDP02YL', 'YLDP02YL', 'color2', 'YLDP06YL', 'color4', 'YLDP13YL', 'color6', 'YLDP13AYL', 'colorc', "YLDP004-A", 'stripe', 'YLDD04YL', 'strip1', 'YLDD01YL', 'YLDD02YL', 'strip4', 'YLDD05YL', 'strip6', 'YLDD05YL']; + // If records are left for configuration if (Object.keys(lights).length > 0) { $('#wh_topcontainer').toggle(false); diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 1f0ac39b..e6e6a2a5 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -231,13 +231,13 @@ "pbrv" : 100 }, - "matrix": - { + "matrix": { "ledshoriz": 1, - "ledsvert" : 1, - "cabling" : "snake", - "start" : "top-left" - } + "ledsvert": 1, + "cabling": "snake", + "direction": "horizontal", + "start": "top-left" + } }, "leds": diff --git a/include/api/API.h b/include/api/API.h index a075b0eb..ff22fea1 100644 --- a/include/api/API.h +++ b/include/api/API.h @@ -336,9 +336,9 @@ protected: bool getUserToken(QString &userToken); /// - /// @brief Is a token authrized. On success this will grant acces to the API (NOT ADMIN API) + /// @brief Is a token authorized. On success this will grant acces to the API (NOT ADMIN API) /// @param token The user Token - /// @return True on succes + /// @return True on success /// bool isTokenAuthorized(const QString &token); diff --git a/include/api/JsonCB.h b/include/api/JsonCB.h index 466db5fa..97dcc3b1 100644 --- a/include/api/JsonCB.h +++ b/include/api/JsonCB.h @@ -17,6 +17,8 @@ // AuthManager #include +#include + class Hyperion; class ComponentRegister; class BonjourBrowserWrapper; @@ -78,10 +80,13 @@ private slots: /// void handleBonjourChange(const QMap& bRegisters); #endif + /// /// @brief handle emits from PriorityMuxer + /// @param currentPriority The current priority at time of emit + /// @param activeInputs The current active input map at time of emit /// - void handlePriorityUpdate(); + void handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs); /// /// @brief Handle imageToLedsMapping updates diff --git a/include/forwarder/MessageForwarder.h b/include/forwarder/MessageForwarder.h index ccce0970..771b7f7e 100644 --- a/include/forwarder/MessageForwarder.h +++ b/include/forwarder/MessageForwarder.h @@ -58,7 +58,7 @@ private slots: /// @brief Handle priority updates from Priority Muxer /// @param priority The new visible priority /// - void handlePriorityChanges(quint8 priority); + void handlePriorityChanges(int priority); /// /// @brief Forward message to all json target hosts diff --git a/include/hyperion/BGEffectHandler.h b/include/hyperion/BGEffectHandler.h index 724f9f0e..da97734d 100644 --- a/include/hyperion/BGEffectHandler.h +++ b/include/hyperion/BGEffectHandler.h @@ -20,14 +20,15 @@ public: , _prioMuxer(_hyperion->getMuxerInstance()) , _isBgEffectConfigured(false) { - // listen for config changes - connect(_hyperion, &Hyperion::settingsChanged, - [=](settings::type type, const QJsonDocument& config) { this->handleSettingsUpdate(type, config); } - ); - connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, - [=]() { this->handlePriorityUpdate(); } - ); + // listen for config changes + connect(_hyperion, &Hyperion::settingsChanged, this, [=] (settings::type type, const QJsonDocument& config) { + this->handleSettingsUpdate(type, config); + }); + + connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, [=] { + this->handlePriorityUpdate(); + }); // initialization handleSettingsUpdate(settings::BGEFFECT, _hyperion->getSetting(settings::BGEFFECT)); @@ -49,7 +50,10 @@ private slots: const QJsonObject& BGEffectConfig = _bgEffectConfig.object(); #define BGCONFIG_ARRAY bgColorConfig.toArray() // clear background priority - _hyperion->clear(PriorityMuxer::BG_PRIORITY); + if (_hyperion->getCurrentPriority() == PriorityMuxer::BG_PRIORITY) + { + _hyperion->clear(PriorityMuxer::BG_PRIORITY); + } // initial background effect/color if (BGEffectConfig["enable"].toBool(true)) { @@ -92,13 +96,14 @@ private slots: /// void handlePriorityUpdate() { - if (_prioMuxer->getCurrentPriority() != PriorityMuxer::BG_PRIORITY && _prioMuxer->hasPriority(PriorityMuxer::BG_PRIORITY)) + if (_prioMuxer->getCurrentPriority() < PriorityMuxer::BG_PRIORITY && _prioMuxer->hasPriority(PriorityMuxer::BG_PRIORITY)) { Debug(Logger::getInstance("HYPERION"),"Stop background (color-) effect as it moved out of scope"); _hyperion->clear(PriorityMuxer::BG_PRIORITY); } else if (_prioMuxer->getCurrentPriority() == PriorityMuxer::LOWEST_PRIORITY && _isBgEffectConfigured) { + Debug(Logger::getInstance("HYPERION"),"Start background (color-) effect as it moved is scope"); emit handleSettingsUpdate (settings::BGEFFECT, _bgEffectConfig); } } diff --git a/include/hyperion/Hyperion.h b/include/hyperion/Hyperion.h index 8a5736ab..2b901862 100644 --- a/include/hyperion/Hyperion.h +++ b/include/hyperion/Hyperion.h @@ -500,7 +500,7 @@ private slots: /// @brief Handle the scenario when no/an input source is available /// @param priority Current priority /// - void handleSourceAvailability(const quint8& priority); + void handleSourceAvailability(int priority); private: friend class HyperionDaemon; diff --git a/include/hyperion/PriorityMuxer.h b/include/hyperion/PriorityMuxer.h index c8af9214..ed8d4fb5 100644 --- a/include/hyperion/PriorityMuxer.h +++ b/include/hyperion/PriorityMuxer.h @@ -54,6 +54,8 @@ public: QString owner; }; + typedef QMap InputsMap; + //Foreground and Background priorities const static int FG_PRIORITY; const static int BG_PRIORITY; @@ -62,6 +64,7 @@ public: const static int LOWEST_PRIORITY; /// Timeout used to identify a non active priority const static int TIMEOUT_NOT_ACTIVE_PRIO; + const static int REMOVE_CLEARED_PRIO; const static int ENDLESS; @@ -197,22 +200,13 @@ public: /// void clearAll(bool forceClearAll=false); - /// - /// @brief Queue a manual push where muxer doesn't recognize them (e.g. continuous single color pushes) - /// - void queuePush() { emit timeRunner(); } - signals: - /// - /// @brief Signal which emits when a effect or color with timeout > -1 is running, once per second - /// - void timeRunner(); /// /// @brief Emits whenever the visible priority has changed /// @param priority The new visible priority /// - void visiblePriorityChanged(quint8 priority); + void visiblePriorityChanged(int priority); /// /// @brief Emits whenever the current visible component changed @@ -222,9 +216,13 @@ signals: /// /// @brief Emits whenever something changes which influences the priorities listing - /// Emits also in 1s interval when a COLOR or EFFECT is running with a timeout > -1 (endless) + + /// Emits also in 1s interval when a COLOR or EFFECT is running with a timeout > -1 + /// @param currentPriority The current priority at time of emit + /// @param activeInputs The current active input map at time of emit + /// - void prioritiesChanged(); + void prioritiesChanged(int currentPriority, InputsMap activeInputs); /// /// internal used signal to resolve treading issues with timer @@ -233,15 +231,15 @@ signals: private slots: /// - /// Slot which is called to adapt to 1s interval for signal timeRunner() / prioritiesChanged() + /// Slot which is called to adapt to 1s interval for signal prioritiesChanged() /// void timeTrigger(); /// - /// Updates the current time. Channels with a configured time out will be checked and cleared if - /// required. + /// Updates the current priorities. Channels with a configured time out will be checked and cleared if + /// required. Cleared priorities will be removed. /// - void setCurrentTime(); + void updatePriorities(); private: /// @@ -266,7 +264,7 @@ private: hyperion::Components _prevVisComp = hyperion::COMP_INVALID; /// The mapping from priority channel to led-information - QMap _activeInputs; + InputsMap _activeInputs; /// The information of the lowest priority channel InputInfo _lowestPriorityInfo; diff --git a/include/utils/NetUtils.h b/include/utils/NetUtils.h index 73417f53..03974748 100644 --- a/include/utils/NetUtils.h +++ b/include/utils/NetUtils.h @@ -29,7 +29,7 @@ namespace NetUtils { server.close(); if(port != prevPort) { - Warning(log, "The requested Port '%d' was already in use, will use Port '%d' instead", prevPort, port); + Warning(log, "The requested Port '%d' is already in use, will use Port '%d' instead", prevPort, port); return false; } return true; diff --git a/libsrc/api/API.cpp b/libsrc/api/API.cpp index a4969978..043b59ac 100644 --- a/libsrc/api/API.cpp +++ b/libsrc/api/API.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -83,15 +84,19 @@ void API::init() } // if this is localConnection and network allows unauth locals, set authorized flag if (apiAuthRequired && _localConnection) + { _authorized = !_authManager->isLocalAuthRequired(); + } // admin access is allowed, when the connection is local and the option for local admin isn't set. Con: All local connections get full access if (_localConnection) { _adminAuthorized = !_authManager->isLocalAdminAuthRequired(); // just in positive direction - if (_adminAuthorized) - _authorized = true; + if (_adminAuthorized) + { + _authorized = true; + } } } @@ -113,12 +118,25 @@ bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &reply // truncate name length data.imgName.truncate(16); - if (data.format == "auto") - { - QImage img = QImage::fromData(data.data); + if (!data.format.isEmpty()) + { + if (data.format == "auto") + { + data.format = ""; + } + else + { + if (!QImageReader::supportedImageFormats().contains(data.format.toLower().toUtf8())) + { + replyMsg = "The given format [" + data.format + "] is not supported"; + return false; + } + } + + QImage img = QImage::fromData(data.data, QSTRING_CSTR(data.format)); if (img.isNull()) { - replyMsg = "Failed to parse picture, the file might be corrupted"; + replyMsg = "Failed to parse picture, the file might be corrupted or content does not match the given format [" + data.format + "]"; return false; } diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 094b0eb0..67ba7317 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -365,20 +365,26 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString QJsonArray priorities; uint64_t now = QDateTime::currentMSecsSinceEpoch(); QList activePriorities = _hyperion->getActivePriorities(); - activePriorities.removeAll(255); + activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY); int currentPriority = _hyperion->getCurrentPriority(); - for(int priority : activePriorities) + for(int priority : qAsConst(activePriorities)) { const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(priority); + QJsonObject item; item["priority"] = priority; - if (priorityInfo.timeoutTime_ms > 0) + + if (priorityInfo.timeoutTime_ms > 0 ) + { item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now); + } // owner has optional informations to the component if (!priorityInfo.owner.isEmpty()) + { item["owner"] = priorityInfo.owner; + } item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); item["origin"] = priorityInfo.origin; @@ -397,7 +403,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString LEDcolor.insert("RGB", RGBValue); uint16_t Hue; - float Saturation, Luminace; + float Saturation; + float Luminace; // add HSL Value to Array QJsonArray HSLValue; diff --git a/libsrc/api/JsonCB.cpp b/libsrc/api/JsonCB.cpp index 5b151784..83be862f 100644 --- a/libsrc/api/JsonCB.cpp +++ b/libsrc/api/JsonCB.cpp @@ -44,6 +44,8 @@ JsonCB::JsonCB(QObject* parent) #if defined(ENABLE_EFFECTENGINE) _availableCommands << "effects-update"; #endif + + qRegisterMetaType("InputsMap"); } bool JsonCB::subscribeFor(const QString& type, bool unsubscribe) @@ -226,25 +228,33 @@ void JsonCB::handleBonjourChange(const QMap& bRegisters) doCallback("sessions-update", QVariant(data)); } #endif -void JsonCB::handlePriorityUpdate() + +void JsonCB::handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs) { QJsonObject data; QJsonArray priorities; uint64_t now = QDateTime::currentMSecsSinceEpoch(); - QList activePriorities = _prioMuxer->getPriorities(); - activePriorities.removeAll(255); - int currentPriority = _prioMuxer->getCurrentPriority(); + QList activePriorities = activeInputs.keys(); + + activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY); + + for (int priority : qAsConst(activePriorities)) { + + const Hyperion::InputInfo& priorityInfo = activeInputs[priority]; - for (int priority : activePriorities) { - const Hyperion::InputInfo priorityInfo = _prioMuxer->getInputInfo(priority); QJsonObject item; item["priority"] = priority; + if (priorityInfo.timeoutTime_ms > 0 ) + { item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now); + } // owner has optional informations to the component if(!priorityInfo.owner.isEmpty()) + { item["owner"] = priorityInfo.owner; + } item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); item["origin"] = priorityInfo.origin; @@ -263,7 +273,8 @@ void JsonCB::handlePriorityUpdate() LEDcolor.insert("RGB", RGBValue); uint16_t Hue; - float Saturation, Luminace; + float Saturation; + float Luminace; // add HSL Value to Array QJsonArray HSLValue; diff --git a/libsrc/boblightserver/BoblightClientConnection.cpp b/libsrc/boblightserver/BoblightClientConnection.cpp index 08b6e202..4610ae91 100644 --- a/libsrc/boblightserver/BoblightClientConnection.cpp +++ b/libsrc/boblightserver/BoblightClientConnection.cpp @@ -55,11 +55,7 @@ BoblightClientConnection::BoblightClientConnection(Hyperion* hyperion, QTcpSocke BoblightClientConnection::~BoblightClientConnection() { - // clear the current channel - if (_priority != 0 && _priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) - _hyperion->clear(_priority); - - delete _socket; + _socket->deleteLater(); } void BoblightClientConnection::readData() @@ -117,9 +113,10 @@ QString BoblightClientConnection::readMessage(const char* data, const size_t siz void BoblightClientConnection::socketClosed() { - // clear the current channel if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) + { _hyperion->clear(_priority); + } emit connectionClosed(this); } @@ -205,40 +202,58 @@ void BoblightClientConnection::handleMessage(const QString& message) { bool rc; const int prio = static_cast(parseUInt(messageParts[2], &rc)); - if (rc && prio != _priority) + if (rc) { - if (_priority != 0 && _hyperion->getPriorityInfo(_priority).componentId == hyperion::COMP_BOBLIGHTSERVER) - _hyperion->clear(_priority); + int currentPriority = _hyperion->getCurrentPriority(); - if (prio < BOBLIGHT_MIN_PRIORITY || prio > BOBLIGHT_MAX_PRIORITY) + if (prio == currentPriority) { - _priority = BOBLIGHT_DEFAULT_PRIORITY; - while (_hyperion->getActivePriorities().contains(_priority)) - { - _priority += 1; - } - - // warn against invalid priority - 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(_socket->peerAddress().toString())); + Error(_log, "The priority %i is already in use onther component of type [%s]", prio, componentToString(_hyperion->getPriorityInfo(currentPriority).componentId)); + _socket->close(); } else { - // register new priority - _hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_socket->peerAddress().toString())); - _priority = prio; - } + if (prio < BOBLIGHT_MIN_PRIORITY || prio > BOBLIGHT_MAX_PRIORITY) + { + _priority = BOBLIGHT_DEFAULT_PRIORITY; + while (_hyperion->getActivePriorities().contains(_priority)) + { + _priority += 1; + } - return; + // warn against invalid priority + 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)); + } + else + { + // register new priority + _hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress)); + _priority = prio; + } + } } + return; } } else if (messageParts[0] == "sync") { if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) - _hyperion->setInput(_priority, _ledColors); // send current color values to hyperion + { + int currentPriority = _hyperion->getCurrentPriority(); + if ( _priority != currentPriority) + { + // register this connection's priority + _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 + } + } return; } @@ -387,6 +402,14 @@ uint8_t BoblightClientConnection::parseByte(const QString& s, bool *ok) const return static_cast(qBound(LO, int(HI * d), HI)); // qBound args are in order min, value, max; see: https://doc.qt.io/qt-5/qtglobal.html#qBound } +void BoblightClientConnection::sendMessage(const QByteArray &message) +{ + if (_socket->isOpen()) + { + _socket->write(message); + } +} + void BoblightClientConnection::sendLightMessage() { char buffer[256]; diff --git a/libsrc/boblightserver/BoblightClientConnection.h b/libsrc/boblightserver/BoblightClientConnection.h index b42387fe..b2e69d14 100644 --- a/libsrc/boblightserver/BoblightClientConnection.h +++ b/libsrc/boblightserver/BoblightClientConnection.h @@ -36,6 +36,13 @@ public: /// ~BoblightClientConnection() override; + /// + /// Get the Boblight client's IP-address + /// + /// @returns IP-address as QString + /// + QString getClientAddress() { return _clientAddress; } + signals: /// /// Signal which is emitted when the connection is being closed @@ -67,7 +74,7 @@ private: /// /// @param message The boblight message to send /// - void sendMessage(const QByteArray &message) { _socket->write(message); }; + void sendMessage(const QByteArray &message); /// /// Send a lights message the to connected client diff --git a/libsrc/boblightserver/BoblightServer.cpp b/libsrc/boblightserver/BoblightServer.cpp index 3495671e..a58257ba 100644 --- a/libsrc/boblightserver/BoblightServer.cpp +++ b/libsrc/boblightserver/BoblightServer.cpp @@ -22,9 +22,12 @@ BoblightServer::BoblightServer(Hyperion* hyperion,const QJsonDocument& config) , _server(new QTcpServer(this)) , _openConnections() , _priority(0) - , _log(Logger::getInstance("BOBLIGHT")) + , _log(nullptr) , _port(0) { + QString subComponent = _hyperion->property("instance").toString(); + _log= Logger::getInstance("BOBLIGHT", subComponent); + Debug(_log, "Instance created"); // listen for component change @@ -49,7 +52,7 @@ void BoblightServer::start() if (NetUtils::portAvailable(_port, _log)) _server->listen(QHostAddress::Any, _port); - Info(_log, "Started on port %d", _port); + Info(_log, "Started on port: %d", _port); _hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening()); } @@ -92,11 +95,9 @@ uint16_t BoblightServer::getPort() const void BoblightServer::newConnection() { QTcpSocket * socket = _server->nextPendingConnection(); - if (socket != nullptr) { - Info(_log, "new connection"); - _hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(socket->peerAddress().toString())); + Info(_log, "New connection from %s ", QSTRING_CSTR(QString("Boblight@%1").arg(socket->peerAddress().toString()))); BoblightClientConnection * connection = new BoblightClientConnection(_hyperion, socket, _priority); _openConnections.insert(connection); @@ -107,7 +108,7 @@ void BoblightServer::newConnection() void BoblightServer::closedConnection(BoblightClientConnection *connection) { - Debug(_log, "connection closed"); + Debug(_log, "Connection closed for %s", QSTRING_CSTR(QString("Boblight@%1").arg(connection->getClientAddress()))); _openConnections.remove(connection); // schedule to delete the connection object diff --git a/libsrc/effectengine/EffectEngine.cpp b/libsrc/effectengine/EffectEngine.cpp index 4e982dad..e4c8c1c8 100644 --- a/libsrc/effectengine/EffectEngine.cpp +++ b/libsrc/effectengine/EffectEngine.cpp @@ -219,7 +219,7 @@ void EffectEngine::effectFinished() _hyperion->clear(effect->getPriority()); } - Info( _log, "effect finished"); + Info( _log, "Effect [%s] finished", QSTRING_CSTR(effect->getName())); for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt) { if (*effectIt == effect) diff --git a/libsrc/forwarder/MessageForwarder.cpp b/libsrc/forwarder/MessageForwarder.cpp index 9abddeea..8a12fb0a 100644 --- a/libsrc/forwarder/MessageForwarder.cpp +++ b/libsrc/forwarder/MessageForwarder.cpp @@ -127,7 +127,7 @@ void MessageForwarder::handleCompStateChangeRequest(hyperion::Components compone } } -void MessageForwarder::handlePriorityChanges(quint8 priority) +void MessageForwarder::handlePriorityChanges(int priority) { const QJsonObject obj = _hyperion->getSetting(settings::NETFORWARD).object(); if (priority != 0 && _forwarder_enabled && obj["enable"].toBool()) diff --git a/libsrc/grabber/qt/QtGrabber.cpp b/libsrc/grabber/qt/QtGrabber.cpp index 25b687cc..b3f35182 100644 --- a/libsrc/grabber/qt/QtGrabber.cpp +++ b/libsrc/grabber/qt/QtGrabber.cpp @@ -66,9 +66,9 @@ bool QtGrabber::open() bool QtGrabber::setupDisplay() { bool result = false; - if ( ! open() ) + if (!open()) { - if ( _isWayland ) + if (_isWayland) { Error(_log, "Grabber does not work under Wayland!"); } @@ -80,45 +80,46 @@ bool QtGrabber::setupDisplay() _numberOfSDisplays = 0; QScreen* primary = QGuiApplication::primaryScreen(); - QList screens = QGuiApplication::screens(); + QList screens = QGuiApplication::screens(); // inject main screen at 0, if not nullptr - if(primary != nullptr) + if (primary != nullptr) { screens.prepend(primary); // remove last main screen if twice in list - if(screens.lastIndexOf(primary) > 0) + if (screens.lastIndexOf(primary) > 0) { screens.removeAt(screens.lastIndexOf(primary)); } } - if(screens.isEmpty()) + if (screens.isEmpty()) { Error(_log, "No displays found to capture from!"); - result = false; + result = false; } else { _numberOfSDisplays = screens.size(); - Info(_log,"Available Displays:"); + Info(_log, "Available Displays:"); int index = 0; - for(auto * screen : qAsConst(screens)) + for (auto* screen : qAsConst(screens)) { const QRect geo = screen->geometry(); - Info(_log,"Display %d: Name: %s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.left(), geo.top() ,geo.right(), geo.bottom(), screen->depth()); + 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()); + ++index; } if (screens.at(0)->size() != screens.at(0)->virtualSize()) { const QRect vgeo = screens.at(0)->virtualGeometry(); - Info(_log,"Display %d: Name: %s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", _numberOfSDisplays, "All Displays", vgeo.left(), vgeo.top() ,vgeo.right(), vgeo.bottom(), screens.at(0)->depth()); + 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()); } _isVirtual = false; // be sure the index is available - if (_display > _numberOfSDisplays - 1 ) + if (_display > _numberOfSDisplays - 1) { if ((screens.at(0)->size() != screens.at(0)->virtualSize()) && (_display == _numberOfSDisplays)) @@ -145,17 +146,17 @@ bool QtGrabber::setupDisplay() } else { - Info(_log,"Initialized display %d", _display); + Info(_log, "Initialized display %d", _display); } - result = true; + result = true; } } return result; } -void QtGrabber::geometryChanged(const QRect &geo) +void QtGrabber::geometryChanged(const QRect& geo) { - Info(_log, "The current display changed geometry to (L,T,R,B) %d,%d,%d,%d", geo.left(), geo.top() ,geo.right(), geo.bottom()); + Info(_log, "The current display changed geometry to (L,T,R,B) %d,%d,%d,%d", geo.left(), geo.top(), geo.x() + geo.width(), geo.y() + geo.height()); updateScreenDimensions(true); } @@ -165,8 +166,6 @@ 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; - int x = xIn; - int y = yIn; HWND hwnd = reinterpret_cast(window); if (hwnd) { @@ -179,15 +178,13 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int hwnd = GetDesktopWindow(); const QRect screenGeometry = _screen->geometry(); windowSize = screenGeometry.size(); - x += screenGeometry.x(); - y += screenGeometry.y(); } if (width < 0) - width = windowSize.width() - x; + width = windowSize.width() - xIn; if (height < 0) - height = windowSize.height() - y; + height = windowSize.height() - yIn; // Create and setup bitmap HDC display_dc = GetDC(nullptr); @@ -197,7 +194,7 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int // copy data HDC window_dc = GetDC(hwnd); - BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY); + BitBlt(bitmap_dc, 0, 0, width, height, window_dc, xIn, yIn, SRCCOPY); // clean up all but bitmap ReleaseDC(hwnd, window_dc); @@ -211,12 +208,12 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int } #endif -int QtGrabber::grabFrame(Image & image) +int QtGrabber::grabFrame(Image& image) { int rc = 0; if (_isEnabled && !_isDeviceInError) { - if(_screen == nullptr) + if (_screen == nullptr) { // reinit, this will disable capture on failure bool result = setupDisplay(); @@ -229,14 +226,14 @@ int QtGrabber::grabFrame(Image & image) QPixmap originalPixmap = grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max); #else QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max); -#endif +#endif if (originalPixmap.isNull()) { rc = -1; } else { - QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat( QImage::Format_RGB888); + QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat(QImage::Format_RGB888); image.resize(static_cast(_calculatedWidth), static_cast(_calculatedHeight)); for (int y = 0; y < imageFrame.height(); y++) @@ -251,7 +248,7 @@ int QtGrabber::grabFrame(Image & image) int QtGrabber::updateScreenDimensions(bool force) { - if(_screen == nullptr) + if (_screen == nullptr) { return -1; } @@ -273,53 +270,65 @@ int QtGrabber::updateScreenDimensions(bool force) } Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _width, _height, geo.width(), geo.height()); - _width = geo.width(); + _width = geo.width(); _height = geo.height(); - int width=0; - int height=0; + int width = 0; + int height = 0; // Image scaling is performed by Qt - width = (_width > (_cropLeft + _cropRight)) + width = (_width > (_cropLeft + _cropRight)) ? ((_width - _cropLeft - _cropRight) / _pixelDecimation) : (_width / _pixelDecimation); - height = (_height > (_cropTop + _cropBottom)) + height = (_height > (_cropTop + _cropBottom)) ? ((_height - _cropTop - _cropBottom) / _pixelDecimation) : (_height / _pixelDecimation); - // calculate final image dimensions and adjust top/left cropping in 3D modes + if (_isVirtual) + { + _src_x = geo.x(); + _src_y = geo.y(); + } + else + { + _src_x = 0; + _src_y = 0; + } + switch (_videoMode) { case VideoMode::VIDEO_3DSBS: - _calculatedWidth = width /2; + _calculatedWidth = width / 2; _calculatedHeight = height; - _src_x = _cropLeft / 2; - _src_y = _cropTop; + _src_x = _src_x + (_cropLeft / 2); + _src_y = _src_y + _cropTop; _src_x_max = (_width / 2) - _cropRight - _cropLeft; _src_y_max = _height - _cropBottom - _cropTop; break; case VideoMode::VIDEO_3DTAB: - _calculatedWidth = width; + _calculatedWidth = width; _calculatedHeight = height / 2; - _src_x = _cropLeft; - _src_y = _cropTop / 2; + _src_x = _src_x + _cropLeft; + _src_y = _src_y + (_cropTop / 2); _src_x_max = _width - _cropRight - _cropLeft; _src_y_max = (_height / 2) - _cropBottom - _cropTop; break; case VideoMode::VIDEO_2D: default: - _calculatedWidth = width; + _calculatedWidth = width; _calculatedHeight = height; - _src_x = _cropLeft; - _src_y = _cropTop; + _src_x = _src_x + _cropLeft; + _src_y = _src_y + _cropTop; _src_x_max = _width - _cropRight - _cropLeft; _src_y_max = _height - _cropBottom - _cropTop; break; } Info(_log, "Update output image resolution to [%dx%d]", _calculatedWidth, _calculatedHeight); + Debug(_log, "Grab screen area: %d,%d,%d,%d", _src_x, _src_y, _src_x_max, _src_y_max); + return 1; } @@ -331,10 +340,10 @@ void QtGrabber::setVideoMode(VideoMode mode) bool QtGrabber::setPixelDecimation(int pixelDecimation) { - bool rc (true); - if(Grabber::setPixelDecimation(pixelDecimation)) + bool rc(true); + if (Grabber::setPixelDecimation(pixelDecimation)) { - if ( updateScreenDimensions(true) < 0) + if (updateScreenDimensions(true) < 0) { rc = false; } @@ -350,14 +359,20 @@ void QtGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBo bool QtGrabber::setDisplayIndex(int index) { - bool rc (true); - if (_display != index) + bool rc(true); + if (_display != index || _isVirtual) { + _isVirtual = false; if (index <= _numberOfSDisplays) { _display = index; + if (index == _numberOfSDisplays) + { + _isVirtual = true; + } } - else { + else + { _display = 0; } rc = setupDisplay(); @@ -370,7 +385,7 @@ QJsonObject QtGrabber::discover(const QJsonObject& params) DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); QJsonObject inputsDiscovered; - if ( open() ) + if (open()) { QList screens = QGuiApplication::screens(); if (!screens.isEmpty()) @@ -390,7 +405,7 @@ QJsonObject QtGrabber::discover(const QJsonObject& params) int pos = name.lastIndexOf('\\'); if (pos != -1) { - name = name.right(name.length()-pos-1); + name = name.right(name.length() - pos - 1); } in["name"] = name; @@ -460,5 +475,4 @@ QJsonObject QtGrabber::discover(const QJsonObject& params) DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); return inputsDiscovered; - } diff --git a/libsrc/hyperion/Hyperion.cpp b/libsrc/hyperion/Hyperion.cpp index f497f244..5d80276e 100644 --- a/libsrc/hyperion/Hyperion.cpp +++ b/libsrc/hyperion/Hyperion.cpp @@ -193,7 +193,10 @@ void Hyperion::stop() void Hyperion::freeObjects() { - // switch off all leds + //delete Background effect first that it does not kick in when other priorities are stopped + delete _BGEffectHandler; + + //Remove all priorities to switch off all leds clear(-1,true); // delete components on exit of hyperion core @@ -462,11 +465,6 @@ void Hyperion::setColor(int priority, const std::vector &ledColors, in } end: - if (getPriorityInfo(priority).componentId != hyperion::COMP_COLOR) - { - clear(priority); - } - // register color registerInput(priority, hyperion::COMP_COLOR, origin); @@ -618,10 +616,9 @@ void Hyperion::handleVisibleComponentChanged(hyperion::Components comp) _raw2ledAdjustment->setBacklightEnabled((comp != hyperion::COMP_COLOR && comp != hyperion::COMP_EFFECT)); } -void Hyperion::handleSourceAvailability(const quint8& priority) +void Hyperion::handleSourceAvailability(int priority) { int previousPriority = _muxer->getPreviousPriority(); - Debug(_log,"priority[%d], previousPriority[%d]", priority, previousPriority); if ( priority == PriorityMuxer::LOWEST_PRIORITY) { Debug(_log,"No source left -> Pause output processing and switch LED-Device off"); diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index fd32c48a..d6d38463 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -17,6 +17,7 @@ const int PriorityMuxer::BG_PRIORITY = 254; const int PriorityMuxer::MANUAL_SELECTED_PRIORITY = 256; const int PriorityMuxer::LOWEST_PRIORITY = std::numeric_limits::max(); const int PriorityMuxer::TIMEOUT_NOT_ACTIVE_PRIO = -100; +const int PriorityMuxer::REMOVE_CLEARED_PRIO = -101; const int PriorityMuxer::ENDLESS = -1; PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) @@ -26,8 +27,6 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) , _previousPriority(_currentPriority) , _manualSelectedPriority(MANUAL_SELECTED_PRIORITY) , _prevVisComp (hyperion::Components::COMP_COLOR) - , _activeInputs() - , _lowestPriorityInfo() , _sourceAutoSelectEnabled(true) , _updateTimer(new QTimer(this)) , _timer(new QTimer(this)) @@ -38,8 +37,10 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) // init lowest priority info _lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY; - _lowestPriorityInfo.timeoutTime_ms = PriorityMuxer::ENDLESS; - _lowestPriorityInfo.ledColors = std::vector(ledCount, {0, 0, 0}); + + _lowestPriorityInfo.timeoutTime_ms = -1; + _lowestPriorityInfo.ledColors = std::vector(ledCount, ColorRgb::BLACK); + _lowestPriorityInfo.componentId = hyperion::COMP_COLOR; _lowestPriorityInfo.origin = "System"; _lowestPriorityInfo.owner = ""; @@ -50,12 +51,10 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) connect(_timer, &QTimer::timeout, this, &PriorityMuxer::timeTrigger); _timer->setSingleShot(true); _blockTimer->setSingleShot(true); - // forward timeRunner signal to prioritiesChanged signal & threading workaround - connect(this, &PriorityMuxer::timeRunner, this, &PriorityMuxer::prioritiesChanged); connect(this, &PriorityMuxer::signalTimeTrigger, this, &PriorityMuxer::timeTrigger); // start muxer timer - connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::setCurrentTime); + connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::updatePriorities); _updateTimer->setInterval(250); _updateTimer->start(); } @@ -85,7 +84,9 @@ bool PriorityMuxer::setSourceAutoSelectEnabled(bool enable, bool update) // update _currentPriority if called from external if(update) - setCurrentTime(); + { + emit prioritiesChanged(_currentPriority,_activeInputs); + } return true; } @@ -128,10 +129,10 @@ bool PriorityMuxer::hasPriority(int priority) const PriorityMuxer::InputInfo PriorityMuxer::getInputInfo(int priority) const { - auto elemIt = _activeInputs.find(priority); + auto elemIt = _activeInputs.constFind(priority); if (elemIt == _activeInputs.end()) { - elemIt = _activeInputs.find(PriorityMuxer::LOWEST_PRIORITY); + elemIt = _activeInputs.constFind(PriorityMuxer::LOWEST_PRIORITY); if (elemIt == _activeInputs.end()) { // fallback @@ -150,11 +151,18 @@ void PriorityMuxer::registerInput(int priority, hyperion::Components component, { // detect new registers bool newInput = false; - bool reusedInput = false; + if (!_activeInputs.contains(priority)) + { newInput = true; + } else if(_prevVisComp == component || _activeInputs[priority].componentId == component) - reusedInput = true; + { + if (_activeInputs[priority].owner != owner) + { + newInput = true; + } + } InputInfo& input = _activeInputs[priority]; input.priority = priority; @@ -166,18 +174,11 @@ void PriorityMuxer::registerInput(int priority, hyperion::Components component, if (newInput) { - Debug(_log,"Register new input '%s/%s' with priority %d as inactive", QSTRING_CSTR(origin), hyperion::componentToIdString(component), priority); - // emit 'prioritiesChanged' only if _sourceAutoSelectEnabled is false - if (!_sourceAutoSelectEnabled) - { - emit prioritiesChanged(); - } - return; + Debug(_log,"Register new input '%s/%s' (%s) with priority %d as inactive", QSTRING_CSTR(origin), hyperion::componentToIdString(component), QSTRING_CSTR(owner), priority); } - - if (reusedInput) + else { - emit timeRunner(); + Debug(_log,"Reuse input '%s/%s' (%s) with priority %d", QSTRING_CSTR(origin), hyperion::componentToIdString(component), QSTRING_CSTR(owner), priority); } } @@ -221,12 +222,12 @@ bool PriorityMuxer::setInput(int priority, const std::vector& ledColor // emit active change if(activeChange) { - Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); - if (_currentPriority < priority) + if (_currentPriority <= priority || !_sourceAutoSelectEnabled) { - emit prioritiesChanged(); + Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); + emit prioritiesChanged(_currentPriority,_activeInputs); } - setCurrentTime(); + updatePriorities(); } return true; @@ -272,12 +273,12 @@ bool PriorityMuxer::setInputImage(int priority, const Image& image, in // emit active change if(activeChange) { - Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); - if (_currentPriority < priority) + if (_currentPriority <= priority || !_sourceAutoSelectEnabled) { - emit prioritiesChanged(); + Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); + emit prioritiesChanged(_currentPriority,_activeInputs); } - setCurrentTime(); + updatePriorities(); } return true; @@ -291,14 +292,9 @@ bool PriorityMuxer::setInputInactive(int priority) bool PriorityMuxer::clearInput(int priority) { - if (priority < PriorityMuxer::LOWEST_PRIORITY && (_activeInputs.remove(priority) > 0)) + if (priority < PriorityMuxer::LOWEST_PRIORITY) { - Debug(_log,"Removed source priority %d",priority); - // on clear success update _currentPriority - setCurrentTime(); - // emit 'prioritiesChanged' only if _sourceAutoSelectEnabled is false - if ((!_sourceAutoSelectEnabled && (_currentPriority < priority)) || _currentPriority == BG_PRIORITY) - emit prioritiesChanged(); + _activeInputs[priority].timeoutTime_ms = REMOVE_CLEARED_PRIO; return true; } return false; @@ -312,6 +308,7 @@ void PriorityMuxer::clearAll(bool forceClearAll) _activeInputs.clear(); _currentPriority = PriorityMuxer::LOWEST_PRIORITY; _activeInputs[_currentPriority] = _lowestPriorityInfo; + updatePriorities(); } else { @@ -326,35 +323,61 @@ void PriorityMuxer::clearAll(bool forceClearAll) } } -void PriorityMuxer::setCurrentTime() +void PriorityMuxer::updatePriorities() { const int64_t now = QDateTime::currentMSecsSinceEpoch(); int newPriority; + bool priorityChanged {false}; + _activeInputs.contains(0) ? newPriority = 0 : newPriority = PriorityMuxer::LOWEST_PRIORITY; - for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();) - { - if (infoIt->timeoutTime_ms > 0 && infoIt->timeoutTime_ms <= now) + QMutableMapIterator i(_activeInputs); + while (i.hasNext()) { + i.next(); + + if ( i.value().timeoutTime_ms == REMOVE_CLEARED_PRIO ) { - int tPrio = infoIt->priority; - infoIt = _activeInputs.erase(infoIt); - Debug(_log,"Timeout clear for priority %d",tPrio); - emit prioritiesChanged(); + int tPrio = i.value().priority; + i.remove(); + + Debug(_log,"Removed source priority %d", tPrio); + priorityChanged = true; } else { - // timeoutTime of TIMEOUT_NOT_ACTIVE_PRIO is awaiting data (inactive); skip - if(infoIt->timeoutTime_ms > TIMEOUT_NOT_ACTIVE_PRIO) - newPriority = qMin(newPriority, infoIt->priority); - - // call timeTrigger when effect or color is running with timeout > 0, blacklist prio 255 - if (infoIt->priority < BG_PRIORITY && infoIt->timeoutTime_ms > 0 && (infoIt->componentId == hyperion::COMP_EFFECT || infoIt->componentId == hyperion::COMP_COLOR || infoIt->componentId == hyperion::COMP_IMAGE)) + if (i.value().timeoutTime_ms > 0 && i.value().timeoutTime_ms <= now) { - emit signalTimeTrigger(); // as signal to prevent Threading issues + //Stop timer for deleted items to avoid additional priority update + _timer->stop(); + int tPrio = i.value().priority; + i.remove(); + + Debug(_log,"Timeout clear for priority %d",tPrio); + priorityChanged = true; + } + else + { + // timeoutTime of TIMEOUT_NOT_ACTIVE_PRIO is awaiting data (inactive); skip + if(i.value().timeoutTime_ms > TIMEOUT_NOT_ACTIVE_PRIO) + { + newPriority = qMin(newPriority, i.value().priority); + } + + // call timeTrigger when effect or color is running with timeout > 0, blacklist prio 255 + if (i.value().priority < BG_PRIORITY && + i.value().timeoutTime_ms > 0 && + ( i.value().componentId == hyperion::COMP_EFFECT || + i.value().componentId == hyperion::COMP_COLOR || + (i.value().componentId == hyperion::COMP_IMAGE && i.value().owner != "Streaming") + ) + ) + { + emit signalTimeTrigger(); // as signal to prevent Threading issues + } } - ++infoIt; } } + // evaluate, if manual selected priority is still available if(!_sourceAutoSelectEnabled) { @@ -371,7 +394,7 @@ void PriorityMuxer::setCurrentTime() } // apply & emit on change (after apply!) hyperion::Components comp = getComponentOfPriority(newPriority); - if (_currentPriority != newPriority || comp != _prevVisComp) + if (_currentPriority != newPriority || comp != _prevVisComp ) { _previousPriority = _currentPriority; _currentPriority = newPriority; @@ -383,7 +406,12 @@ void PriorityMuxer::setCurrentTime() _prevVisComp = comp; emit visibleComponentChanged(comp); } - emit prioritiesChanged(); + priorityChanged = true; + } + + if (priorityChanged) + { + emit prioritiesChanged(_currentPriority,_activeInputs); } } @@ -395,7 +423,7 @@ void PriorityMuxer::timeTrigger() } else { - emit timeRunner(); _blockTimer->start(1000); + emit prioritiesChanged(_currentPriority,_activeInputs); } } diff --git a/libsrc/hyperion/schema/schema-boblightServer.json b/libsrc/hyperion/schema/schema-boblightServer.json index 22fb24c9..abf44922 100644 --- a/libsrc/hyperion/schema/schema-boblightServer.json +++ b/libsrc/hyperion/schema/schema-boblightServer.json @@ -15,6 +15,7 @@ "type" : "integer", "required" : true, "title" : "edt_conf_general_port_title", + "default" : 19333, "minimum" : 1024, "maximum" : 65535, "propertyOrder" : 2 diff --git a/libsrc/hyperion/schema/schema-ledConfig.json b/libsrc/hyperion/schema/schema-ledConfig.json index 5e9651e5..3232063c 100644 --- a/libsrc/hyperion/schema/schema-ledConfig.json +++ b/libsrc/hyperion/schema/schema-ledConfig.json @@ -137,6 +137,10 @@ "type": "string", "enum": [ "snake", "parallel" ] }, + "direction": { + "type": "string", + "enum": [ "horizontal", "vertical" ] + }, "start": { "type": "string", "enum": [ "top-left", "top-right", "bottom-left", "bottom-right" ] diff --git a/libsrc/leddevice/dev_net/LedDeviceWled.cpp b/libsrc/leddevice/dev_net/LedDeviceWled.cpp index 45ad2d31..3998311f 100644 --- a/libsrc/leddevice/dev_net/LedDeviceWled.cpp +++ b/libsrc/leddevice/dev_net/LedDeviceWled.cpp @@ -357,8 +357,10 @@ QJsonObject LedDeviceWled::getProperties(const QJsonObject& params) } QJsonObject propertiesDetails = response.getBody().object(); - propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM); - + if (!propertiesDetails.isEmpty()) + { + propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM); + } properties.insert("properties", propertiesDetails); DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() ); diff --git a/libsrc/webserver/QtHttpClientWrapper.cpp b/libsrc/webserver/QtHttpClientWrapper.cpp index f308a9f4..3bf2bdef 100644 --- a/libsrc/webserver/QtHttpClientWrapper.cpp +++ b/libsrc/webserver/QtHttpClientWrapper.cpp @@ -98,10 +98,14 @@ void QtHttpClientWrapper::onClientDataReceived (void) if (pos > 0) { - QByteArray header = raw.left (pos).trimmed (); - QByteArray value = raw.mid (pos +1).trimmed (); + QByteArray header = raw.left (pos).trimmed(); + QByteArray value = raw.mid (pos +1).trimmed(); m_currentRequest->addHeader (header, value); - if (header == QtHttpHeader::ContentLength) + #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + if (header.compare(QtHttpHeader::ContentLength, Qt::CaseInsensitive) == 0) + #else + if (header.toLower() == QtHttpHeader::ContentLength.toLower()) + #endif { bool ok = false; const int len = value.toInt (&ok, 10); @@ -153,7 +157,7 @@ void QtHttpClientWrapper::onClientDataReceived (void) case RequestParsed: // a valid request has ben fully parsed { // Catch websocket header "Upgrade" - if(m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower() == "websocket") + if(m_currentRequest->getHeader(QtHttpHeader::Upgrade) == "websocket") { if(m_websocketClient == Q_NULLPTR) { @@ -327,7 +331,7 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHtt { static const QByteArray & CLOSE = QByteArrayLiteral ("close"); - if (m_currentRequest->getHeader (QtHttpHeader::Connection).toLower () == CLOSE) + if (m_currentRequest->getHeader(QtHttpHeader::Connection) == CLOSE) { // must close connection after this request m_sockClient->close (); diff --git a/libsrc/webserver/QtHttpRequest.cpp b/libsrc/webserver/QtHttpRequest.cpp index 0adc8c4e..45d3bb75 100644 --- a/libsrc/webserver/QtHttpRequest.cpp +++ b/libsrc/webserver/QtHttpRequest.cpp @@ -25,7 +25,7 @@ void QtHttpRequest::setClientInfo (const QHostAddress & server, const QHostAddre void QtHttpRequest::addHeader (const QByteArray & header, const QByteArray & value) { - QByteArray key = header.trimmed (); + QByteArray key = header.trimmed().toLower(); if (!key.isEmpty ()) { diff --git a/libsrc/webserver/QtHttpRequest.h b/libsrc/webserver/QtHttpRequest.h index df4ad2d2..28a8fb67 100644 --- a/libsrc/webserver/QtHttpRequest.h +++ b/libsrc/webserver/QtHttpRequest.h @@ -38,7 +38,7 @@ public: QByteArray getHeader (const QByteArray & header) const { - return m_headersHash.value (header, QByteArray ()); + return m_headersHash.value (header.toLower(), QByteArray ()); }; public slots: diff --git a/src/hyperion-remote/JsonConnection.cpp b/src/hyperion-remote/JsonConnection.cpp index 6c139de4..7ec625df 100644 --- a/src/hyperion-remote/JsonConnection.cpp +++ b/src/hyperion-remote/JsonConnection.cpp @@ -68,7 +68,7 @@ void JsonConnection::setColor(std::vector colors, int priority, int dura parseReply(reply); } -void JsonConnection::setImage(QImage &image, int priority, int duration) +void JsonConnection::setImage(QImage &image, int priority, int duration, const QString& name) { Debug(_log, "Set image has size: %dx%d", image.width(), image.height()); @@ -93,6 +93,8 @@ void JsonConnection::setImage(QImage &image, int priority, int duration) command["command"] = QString("image"); command["priority"] = priority; command["origin"] = QString("hyperion-remote"); + if (!name.isEmpty()) + command["name"] = name; command["imagewidth"] = image.width(); command["imageheight"] = image.height(); command["imagedata"] = QString(base64Image.data()); diff --git a/src/hyperion-remote/JsonConnection.h b/src/hyperion-remote/JsonConnection.h index c04a52d8..111aab42 100644 --- a/src/hyperion-remote/JsonConnection.h +++ b/src/hyperion-remote/JsonConnection.h @@ -41,13 +41,14 @@ public: void setColor(std::vector color, int priority, int duration); /// - /// Set the leds according to the given image (assume the image is stretched to the display size) + /// Set the LEDs according to the given image (assume the image is stretched to the display size) /// /// @param image The image /// @param priority The priority /// @param duration The duration in milliseconds + /// @param name The image's filename /// - void setImage(QImage &image, int priority, int duration); + void setImage(QImage &image, int priority, int duration, const QString& name = ""); #if defined(ENABLE_EFFECTENGINE) /// diff --git a/src/hyperion-remote/hyperion-remote.cpp b/src/hyperion-remote/hyperion-remote.cpp index 789aa458..97ecd3c1 100644 --- a/src/hyperion-remote/hyperion-remote.cpp +++ b/src/hyperion-remote/hyperion-remote.cpp @@ -252,7 +252,8 @@ int main(int argc, char * argv[]) } else if (parser.isSet(argImage)) { - connection.setImage(argImage.getImage(parser), argPriority.getInt(parser), argDuration.getInt(parser)); + QFileInfo imageFile {argImage.getCString(parser)}; + connection.setImage(argImage.getImage(parser), argPriority.getInt(parser), argDuration.getInt(parser), imageFile.fileName()); } #if defined(ENABLE_EFFECTENGINE) else if (parser.isSet(argEffect)) diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index 1bd30eee..d9a57e48 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -103,23 +103,7 @@ QCoreApplication* createApplication(int &argc, char *argv[]) #else if (!forceNoGui) { - // if x11, then test if xserver is available - #if defined(ENABLE_X11) - Display* dpy = XOpenDisplay(NULL); - if (dpy != NULL) - { - XCloseDisplay(dpy); - isGuiApp = true; - } - #elif defined(ENABLE_XCB) - int screen_num; - xcb_connection_t * connection = xcb_connect(nullptr, &screen_num); - if (!xcb_connection_has_error(connection)) - { - isGuiApp = true; - } - xcb_disconnect(connection); - #endif + isGuiApp = (getenv("DISPLAY") != NULL && (getenv("XDG_SESSION_TYPE") != NULL || getenv("WAYLAND_DISPLAY") != NULL)); } #endif @@ -397,7 +381,7 @@ int main(int argc, char** argv) } } - Info(log,"Starting Hyperion - %s, %s, built: %s:%s", HYPERION_VERSION, HYPERION_BUILD_ID, __DATE__, __TIME__); + Info(log,"Starting Hyperion [%sGUI mode] - %s, %s, built: %s:%s", isGuiApp ? "": "non-", HYPERION_VERSION, HYPERION_BUILD_ID, __DATE__, __TIME__); Debug(log,"QtVersion [%s]", QT_VERSION_STR); if ( !readonlyMode ) @@ -423,7 +407,7 @@ int main(int argc, char** argv) // run the application if (isGuiApp) { - Info(log, "start systray"); + Info(log, "Start Systray menu"); QApplication::setQuitOnLastWindowClosed(false); SysTray tray(hyperiond); tray.hide();