UI and Web updates (#1421)

* Stop Web-Capture when priority changes

* Remote control UI: Treat duration=0 as endless

* Stop Web-Capture on non-Image events changes

* LED Matrix Layout - Support vertical cabling direction

* Additional Yeelight models

* Treat http headers case insensitive

* Update change log

* Treat http headers case insensitive (consider Qt version)

* API - Consider provided format when setImage

* UI - Support Boblight configuration per LED instance

* Support multiple Boblight clients with different priorities

* Update changelog

* Simplify isGUI rules allowing for QT only builds

* Sysinfo: Fix indents

* LED-Devices: Show warning, if get properties failed

* Qt-Grabber: Fixed position handling of multiple monitors

* LED layout: Remove indention limitations

* Yeelight: Test YLTD003

* hyperion-remote: Provide image filename to muxer/UI

* Refactor PriorityMuxer and related

* Temp: Build under Windows 2019

* Yeelight: Remove YLTD003 as it is not working without additional changes

* Test Windows-latest with out removing redistributables/new MSVC

* correct workflows

* correct CI script

* Build Windows with Qt 5.15.2

* Priority Muxer: Updates after testing

* Fix Typo

* Update BGHandler

* QTGrabber - Reactivate windows code to avoid cursor issues

* Emit prioritiesChanged when autoselect was changed by user

Co-authored-by: Paulchen Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
LordGrey 2022-02-22 20:58:59 +01:00 committed by GitHub
parent 0a3df596cf
commit 160c5d0b3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 775 additions and 512 deletions

View File

@ -35,7 +35,7 @@ elif [[ $CI_NAME == *"mingw64_nt"* || "$CI_NAME" == 'windows_nt' ]]; then
echo "Number of Cores $NUMBER_OF_PROCESSORS" echo "Number of Cores $NUMBER_OF_PROCESSORS"
mkdir build || exit 1 mkdir build || exit 1
cd build 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 cmake --build . --target package --config Release -- -nologo -v:m -maxcpucount || exit 3
exit 0; exit 0;
exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; } exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; }

View File

@ -121,10 +121,10 @@ jobs:
windows: windows:
name: Windows name: Windows
runs-on: windows-latest runs-on: windows-2022
env: env:
VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC' VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC'
QT_VERSION: 5.15.0 QT_VERSION: 5.15.2
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v1 uses: actions/checkout@v1
@ -159,12 +159,6 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
key: ${{ runner.os }}-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 - name: Install Python, NSIS, OpenSSL, DirectX SDK
shell: powershell shell: powershell
run: | run: |

View File

@ -91,10 +91,10 @@ jobs:
windows: windows:
name: Windows name: Windows
runs-on: windows-latest runs-on: windows-2022
env: env:
VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC' VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC'
QT_VERSION: 5.15.0 QT_VERSION: 5.15.2
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v1 uses: actions/checkout@v1
@ -122,12 +122,6 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
key: ${{ runner.os }}-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 - name: Install Python, NSIS, OpenSSL, DirectX SDK
shell: powershell shell: powershell
run: | run: |

View File

@ -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 build a "light" version of Hyperion, i.e. no grabbers, or services like flat-/proto buffers, boblight, CEC
- Allow to restart Hyperion via Systray - 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 ### Changed
- Colors Smoothing is started in pause mode to save resources, when Hyperion starts with no active source - 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 ### Fixed
- Effects: Fix image URL in Matrix effect - 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 - 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) - 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 - 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 - 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 ## Removed

View File

@ -2,7 +2,7 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<h3 class="page-header"><i class="fa fa-camera fa-fw"></i><span data-i18n="main_menu_grabber_conf_token">Capturing Hardware</span></h3> <h3 class="page-header"><i class="fa fa-camera fa-fw"></i><span data-i18n="main_menu_instcapture_conf_token">Capturing Hardware</span></h3>
<div class="panel panel-default" style="border:0px;"> <div class="panel panel-default" style="border:0px;">
<div class="panel-heading panel-instance" style="border-radius:3px; border-bottom:0px;"> <div class="panel-heading panel-instance" style="border-radius:3px; border-bottom:0px;">

View File

@ -199,14 +199,14 @@
<label class="ltdlabel" for="ip_cl_ptl" data-i18n="conf_leds_layout_ptl">Point Top Left</label> <label class="ltdlabel" for="ip_cl_ptl" data-i18n="conf_leds_layout_ptl">Point Top Left</label>
</td> </td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_ptlh" type="number" value="0" min="0" max="40" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_ptlh" type="number" value="0" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_h">%h</div> <div class="input-group-addon" data-i18n="edt_append_percent_h">%h</div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="ltd"></td> <td class="ltd"></td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_ptlv" type="number" value="0" min="0" max="40" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_ptlv" type="number" value="0" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_v">%v</div> <div class="input-group-addon" data-i18n="edt_append_percent_v">%v</div>
</td> </td>
</tr> </tr>
@ -215,14 +215,14 @@
<label class="ltdlabel" for="ip_cl_ptr" data-i18n="conf_leds_layout_ptr">Point Top Right</label> <label class="ltdlabel" for="ip_cl_ptr" data-i18n="conf_leds_layout_ptr">Point Top Right</label>
</td> </td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_ptrh" type="number" value="100" min="60" max="100" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_ptrh" type="number" value="100" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_h">%</div> <div class="input-group-addon" data-i18n="edt_append_percent_h">%</div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="ltd"></td> <td class="ltd"></td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_ptrv" type="number" value="0" min="0" max="40" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_ptrv" type="number" value="0" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_v">%</div> <div class="input-group-addon" data-i18n="edt_append_percent_v">%</div>
</td> </td>
</tr> </tr>
@ -231,14 +231,14 @@
<label class="ltdlabel" for="ip_cl_pbr" data-i18n="conf_leds_layout_pbr">Point Bottom Right</label> <label class="ltdlabel" for="ip_cl_pbr" data-i18n="conf_leds_layout_pbr">Point Bottom Right</label>
</td> </td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_pbrh" type="number" value="100" min="60" max="100" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_pbrh" type="number" value="100" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_h">%</div> <div class="input-group-addon" data-i18n="edt_append_percent_h">%</div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="ltd"></td> <td class="ltd"></td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_pbrv" type="number" value="100" min="60" max="100" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_pbrv" type="number" value="100" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_v">%</div> <div class="input-group-addon" data-i18n="edt_append_percent_v">%</div>
</td> </td>
</tr> </tr>
@ -247,14 +247,14 @@
<label class="ltdlabel" for="ip_cl_pbl" data-i18n="conf_leds_layout_pbl">Point Bottom Left</label> <label class="ltdlabel" for="ip_cl_pbl" data-i18n="conf_leds_layout_pbl">Point Bottom Left</label>
</td> </td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_pblh" type="number" value="0" min="0" max="40" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_pblh" type="number" value="0" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_h">%</div> <div class="input-group-addon" data-i18n="edt_append_percent_h">%</div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="ltd"></td> <td class="ltd"></td>
<td class="itd input-group"> <td class="itd input-group">
<input class="form-control ledCLconstr" id="ip_cl_pblv" type="number" value="100" min="60" max="100" step="1"></input> <input class="form-control ledCLconstr" id="ip_cl_pblv" type="number" value="100" min="0" max="100" step="1"></input>
<div class="input-group-addon" data-i18n="edt_append_percent_v">%</div> <div class="input-group-addon" data-i18n="edt_append_percent_v">%</div>
</td> </td>
</tr> </tr>
@ -402,3 +402,4 @@
<script src="/js/content_leds.js"></script> <script src="/js/content_leds.js"></script>

View File

@ -70,6 +70,14 @@
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemNetwork', 'editor_container_protoserver')" style="text-decoration: none; cursor: pointer"></a> <a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemNetwork', 'editor_container_protoserver')" style="text-decoration: none; cursor: pointer"></a>
</td> </td>
</tr> </tr>
<tr id="dash_ports_boblight_row">
<td></td>
<td data-i18n="dashboard_infobox_label_port_boblight">proto</td>
<td style="text-align: right; padding-right: 0">
<span id="dash_boblightPort">unknown</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemInstCapture', 'editor_container_boblightserver')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr>
<tr> <tr>
<td></td> <td></td>
<td data-i18n="dashboard_infobox_label_port_json">json</td> <td data-i18n="dashboard_infobox_label_port_json">json</td>

View File

@ -55,6 +55,8 @@
"conf_leds_contr_label_contrtype": "Controller type:", "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_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_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),<br>$3 {{plural:$3|LED|LEDs}} will stay black if you continue.", "conf_leds_error_hwled_gt_layout": "The hardware LED count ($1) is greater than LEDs configured via layout ($2),<br>$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). <br> The number of LEDs configured in the layout must not exceed the available LEDs", "conf_leds_error_hwled_lt_layout": "The hardware LED count ($1) is less than LEDs configured via layout ($2). <br> 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). <br> The hardware LED count is set to ($3).", "conf_leds_error_hwled_gt_maxled": "The hardware LED count ($1) is greater than the maximum number of LEDs supported by the device ($2). <br> The hardware LED count is set to ($3).",
@ -99,6 +101,7 @@
"conf_leds_layout_generatedconf": "Generated/Current LED Configuration", "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_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_cabling": "Cabling",
"conf_leds_layout_ma_direction": "Direction",
"conf_leds_layout_ma_horiz": "Horizontal", "conf_leds_layout_ma_horiz": "Horizontal",
"conf_leds_layout_ma_optbottomleft": "Bottom left", "conf_leds_layout_ma_optbottomleft": "Bottom left",
"conf_leds_layout_ma_optbottomright": "Bottom right", "conf_leds_layout_ma_optbottomright": "Bottom right",
@ -185,6 +188,7 @@
"dashboard_infobox_label_instance": "Instance:", "dashboard_infobox_label_instance": "Instance:",
"dashboard_infobox_label_latesthyp": "Latest Hyperion version:", "dashboard_infobox_label_latesthyp": "Latest Hyperion version:",
"dashboard_infobox_label_platform": "Platform:", "dashboard_infobox_label_platform": "Platform:",
"dashboard_infobox_label_port_boblight": "Boblight Server:",
"dashboard_infobox_label_port_flat": "Flatbuffer:", "dashboard_infobox_label_port_flat": "Flatbuffer:",
"dashboard_infobox_label_port_json": "JSON-Server:", "dashboard_infobox_label_port_json": "JSON-Server:",
"dashboard_infobox_label_port_proto": "Protobuffer:", "dashboard_infobox_label_port_proto": "Protobuffer:",

View File

@ -138,6 +138,13 @@ $(document).ready(function () {
$("#dash_ports_proto_row").hide(); $("#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; var jsonPort = window.serverConfig.jsonServer.port;
$('#dash_jsonPort').html(jsonPort); $('#dash_jsonPort').html(jsonPort);
var wsPorts = window.serverConfig.webConfig.port + ' | ' + window.serverConfig.webConfig.sslPort; var wsPorts = window.serverConfig.webConfig.port + ' | ' + window.serverConfig.webConfig.sslPort;

View File

@ -226,8 +226,10 @@ $(document).ready(function () {
//Hide capture menu entries, if no grabbers are available //Hide capture menu entries, if no grabbers are available
if ((window.serverInfo.grabbers.screen.available.length === 0) && (window.serverInfo.grabbers.video.available.length === 0)) { if ((window.serverInfo.grabbers.screen.available.length === 0) && (window.serverInfo.grabbers.video.available.length === 0)) {
$("#MenuItemGrabber").attr('style', 'display:none') $("#MenuItemGrabber").attr('style', 'display:none')
if ((jQuery.inArray("boblight", window.serverInfo.services) === -1)) {
$("#MenuItemInstCapture").attr('style', 'display:none') $("#MenuItemInstCapture").attr('style', 'display:none')
} }
}
//Hide effectsconfigurator menu entry, if effectengine is not available //Hide effectsconfigurator menu entry, if effectengine is not available
if (jQuery.inArray("effectengine", window.serverInfo.services) === -1) { if (jQuery.inArray("effectengine", window.serverInfo.services) === -1) {

View File

@ -4,17 +4,40 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0); var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.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 // update instance listing
updateHyperionInstanceListing(); updateHyperionInstanceListing();
var conf_editor_instCapt = null; var conf_editor_instCapt = null;
var conf_editor_bobl = null;
// Instance Capture // Instance Capture
if (window.showOptHelp) {
if (screenGrabberAvailable || videoGrabberAvailable) {
$('#conf_cont').append(createRow('conf_cont_instCapt')); $('#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(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"))); $('#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', ''));
}
}
if (screenGrabberAvailable || videoGrabberAvailable) {
// Instance Capture // Instance Capture
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', { conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
@ -91,7 +114,6 @@ $(document).ready(function () {
}); });
conf_editor_instCapt.watch('root.instCapture.v4lEnable', () => { conf_editor_instCapt.watch('root.instCapture.v4lEnable', () => {
console.log("instCapt.watch(root.instCapture.v4lEnable");
var videoEnable = conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").getValue(); var videoEnable = conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").getValue();
if (videoEnable) { if (videoEnable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").setValue(window.serverConfig.grabberV4L2.available_devices); conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").setValue(window.serverConfig.grabberV4L2.available_devices);
@ -111,6 +133,55 @@ $(document).ready(function () {
$('#btn_submit_instCapt').off().on('click', function () { $('#btn_submit_instCapt').off().on('click', function () {
requestWriteConfig(conf_editor_instCapt.getValue()); 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(); removeOverlay();
}); });

View File

@ -277,7 +277,7 @@ function createClassicLeds() {
aceEdt.set(finalLedArray); 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 // Big thank you to RanzQ (Juha Rantanen) from Github for this script
// https://raw.githubusercontent.com/RanzQ/hyperion-audio-effects/master/matrix-config.js // https://raw.githubusercontent.com/RanzQ/hyperion-audio-effects/master/matrix-config.js
@ -325,6 +325,20 @@ function createMatrixLayout(ledshoriz, ledsvert, cabling, start) {
var x, y var x, y
if (direction === 'vertical') {
for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) {
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
}
}
} else {
for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) { 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) { for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) {
addLed(x, y) addLed(x, y)
@ -336,6 +350,7 @@ function createMatrixLayout(ledshoriz, ledsvert, cabling, start) {
endX = tmp endX = tmp
} }
} }
}
return leds; return leds;
} }
@ -348,9 +363,10 @@ function createMatrixLeds() {
var ledshoriz = parseInt($("#ip_ma_ledshoriz").val()); var ledshoriz = parseInt($("#ip_ma_ledshoriz").val());
var ledsvert = parseInt($("#ip_ma_ledsvert").val()); var ledsvert = parseInt($("#ip_ma_ledsvert").val());
var cabling = $("#ip_ma_cabling").val(); var cabling = $("#ip_ma_cabling").val();
var direction = $("#ip_ma_direction").val();
var start = $("#ip_ma_start").val(); var start = $("#ip_ma_start").val();
nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start); nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction);
finalLedArray = blackListLeds(nonBlacklistLedArray, ledBlacklist); finalLedArray = blackListLeds(nonBlacklistLedArray, ledBlacklist);
createLedPreview(finalLedArray, 'matrix'); createLedPreview(finalLedArray, 'matrix');
@ -467,7 +483,63 @@ $(document).ready(function () {
// bind change event to all inputs // bind change event to all inputs
$('.ledCLconstr').bind("change", function () { $('.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(); createClassicLeds();
}); });
@ -1591,6 +1663,7 @@ async function getProperties_device(ledType, key, params) {
} }
} }
else { else {
showNotification('warning', $.i18n('conf_leds_error_get_properties_text'), $.i18n('conf_leds_error_get_properties_title'))
$('#btn_submit_controller').attr('disabled', true); $('#btn_submit_controller').attr('disabled', true);
$('#btn_test_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_ledshoriz").val(ledProperties.maxColumn);
$("#ip_ma_ledsvert").val(ledProperties.maxRow); $("#ip_ma_ledsvert").val(ledProperties.maxRow);
$("#ip_ma_cabling").val("parallel"); $("#ip_ma_cabling").val("parallel");
$("#ip_ma_direction").val("horizontal");
$("#ip_ma_start").val("top-left"); $("#ip_ma_start").val("top-left");
createMatrixLeds(); createMatrixLeds();
} }

View File

@ -1,7 +1,6 @@
$(document).ready(function () { $(document).ready(function () {
performTranslation(); performTranslation();
var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1);
var FORWARDER_ENABLED = (jQuery.inArray("forwarder", 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 FLATBUF_SERVER_ENABLED = (jQuery.inArray("flatbuffer", window.serverInfo.services) !== -1);
var PROTOTBUF_SERVER_ENABLED = (jQuery.inArray("protobuffer", 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_json = null;
var conf_editor_proto = null; var conf_editor_proto = null;
var conf_editor_fbs = null; var conf_editor_fbs = null;
var conf_editor_bobl = null;
var conf_editor_forw = null; var conf_editor_forw = null;
addJsonEditorHostValidation(); addJsonEditorHostValidation();
@ -40,13 +38,6 @@ $(document).ready(function () {
$('#conf_cont_proto').append(createHelpTable(window.schema.protoServer.properties, $.i18n("edt_conf_pbs_heading_title"), "protoServerHelpPanelId")); $('#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 //forwarder
if (FORWARDER_ENABLED) { if (FORWARDER_ENABLED) {
if (storedAccess != 'default') { if (storedAccess != 'default') {
@ -66,9 +57,6 @@ $(document).ready(function () {
if (PROTOTBUF_SERVER_ENABLED) { if (PROTOTBUF_SERVER_ENABLED) {
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_pbs_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver')); $('#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) { if (FORWARDER_ENABLED) {
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fw_heading_title"), 'editor_container_forwarder', 'btn_submit_forwarder')); $('#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 //forwarder
if (FORWARDER_ENABLED) { if (FORWARDER_ENABLED) {
if (storedAccess != 'default') { if (storedAccess != 'default') {
@ -205,9 +170,6 @@ $(document).ready(function () {
if (PROTOTBUF_SERVER_ENABLED) { if (PROTOTBUF_SERVER_ENABLED) {
createHint("intro", $.i18n('conf_network_proto_intro'), "editor_container_protoserver"); 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) { if (FORWARDER_ENABLED) {
createHint("intro", $.i18n('conf_network_forw_intro'), "editor_container_forwarder"); createHint("intro", $.i18n('conf_network_forw_intro'), "editor_container_forwarder");
} }

View File

@ -134,7 +134,7 @@ $(document).ready(function () {
owner = $.i18n('remote_color_label_color') + ' ' + '<div style="width:18px; height:18px; border-radius:20px; margin-bottom:-4px; border:1px grey solid; background-color: rgb(' + value + '); display:inline-block" title="RGB: (' + value + ')"></div>'; owner = $.i18n('remote_color_label_color') + ' ' + '<div style="width:18px; height:18px; border-radius:20px; margin-bottom:-4px; border:1px grey solid; background-color: rgb(' + value + '); display:inline-block" title="RGB: (' + value + ')"></div>';
break; break;
case "IMAGE": case "IMAGE":
owner = $.i18n('remote_effects_label_picture') + ' ' + owner; owner = $.i18n('remote_effects_label_picture') + (owner !== undefined ? (' ' + owner): "");
break; break;
case "GRABBER": case "GRABBER":
owner = $.i18n('general_comp_GRABBER') + ': (' + owner + ')'; owner = $.i18n('general_comp_GRABBER') + ': (' + owner + ')';
@ -153,6 +153,8 @@ $(document).ready(function () {
break; break;
} }
if (!(duration && duration < 0))
{
if (duration && compId != "GRABBER" && compId != "FLATBUFSERVER" && compId != "PROTOSERVER") if (duration && compId != "GRABBER" && compId != "FLATBUFSERVER" && compId != "PROTOSERVER")
owner += '<br/><span style="font-size:80%; color:grey;">' + $.i18n('remote_input_duration') + ' ' + duration.toFixed(0) + $.i18n('edt_append_s') + '</span>'; owner += '<br/><span style="font-size:80%; color:grey;">' + $.i18n('remote_input_duration') + ' ' + duration.toFixed(0) + $.i18n('edt_append_s') + '</span>';
@ -164,6 +166,7 @@ $(document).ready(function () {
if (btn_type != 'default') if (btn_type != 'default')
$('.sstbody').append(createTableRow([origin, owner, priority, btn], false, true)); $('.sstbody').append(createTableRow([origin, owner, priority, btn], false, true));
} }
}
var btn_auto_color = (window.serverInfo.priorities_autoselect ? "btn-success" : "btn-danger"); var btn_auto_color = (window.serverInfo.priorities_autoselect ? "btn-success" : "btn-danger");
var btn_auto_state = (window.serverInfo.priorities_autoselect ? "disabled" : "enabled"); var btn_auto_state = (window.serverInfo.priorities_autoselect ? "disabled" : "enabled");
var btn_auto_text = (window.serverInfo.priorities_autoselect ? $.i18n('general_btn_on') : $.i18n('general_btn_off')); var btn_auto_text = (window.serverInfo.priorities_autoselect ? $.i18n('general_btn_on') : $.i18n('general_btn_off'));
@ -313,6 +316,9 @@ $(document).ready(function () {
if (getStorage('rmduration') != null) { if (getStorage('rmduration') != null) {
$("#remote_duration").val(getStorage('rmduration')); $("#remote_duration").val(getStorage('rmduration'));
duration = getStorage('rmduration'); duration = getStorage('rmduration');
if (duration == 0) {
duration = ENDLESS;
}
} }
createCP('cp2', cpcolor, function (rgbT, hex) { createCP('cp2', cpcolor, function (rgbT, hex) {
@ -332,6 +338,9 @@ $(document).ready(function () {
$("#remote_duration").off().on("change", function () { $("#remote_duration").off().on("change", function () {
duration = valValue(this.id, this.value, this.min, this.max); duration = valValue(this.id, this.value, this.min, this.max);
setStorage('rmduration', duration); setStorage('rmduration', duration);
if (duration == 0) {
duration = ENDLESS;
}
}); });
$("#effect_select").off().on("change", function (event) { $("#effect_select").off().on("change", function (event) {
@ -349,8 +358,9 @@ $(document).ready(function () {
}); });
$("#remote_input_repimg").off().on("click", function () { $("#remote_input_repimg").off().on("click", function () {
if (lastImgData != "") if (lastImgData != "") {
requestSetImage(lastImgData, duration, lastFileName); requestSetImage(lastImgData, duration, lastFileName);
}
}); });
$("#remote_input_img").change(function () { $("#remote_input_img").change(function () {
@ -380,7 +390,6 @@ $(document).ready(function () {
// interval updates // interval updates
$(window.hyperion).on('components-updated', function (e, comp) { $(window.hyperion).on('components-updated', function (e, comp) {
//console.log ("components-updated", e, comp);
updateComponent(comp); updateComponent(comp);
}); });

View File

@ -359,21 +359,25 @@ function requestPriorityClear(prio)
if(typeof prio !== 'number') if(typeof prio !== 'number')
prio = window.webPrio; prio = window.webPrio;
$(window.hyperion).trigger({type:"stopBrowerScreenCapture"});
sendToHyperion("clear", "", '"priority":'+prio+''); sendToHyperion("clear", "", '"priority":'+prio+'');
} }
function requestClearAll() function requestClearAll()
{ {
$(window.hyperion).trigger({type:"stopBrowerScreenCapture"});
requestPriorityClear(-1) requestPriorityClear(-1)
} }
function requestPlayEffect(effectName, duration) function requestPlayEffect(effectName, duration)
{ {
$(window.hyperion).trigger({type:"stopBrowerScreenCapture"});
sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'"},"priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"'); sendToHyperion("effect", "", '"effect":{"name":"'+effectName+'"},"priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"');
} }
function requestSetColor(r,g,b,duration) 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+'"'); sendToHyperion("color", "", '"color":['+r+','+g+','+b+'], "priority":'+window.webPrio+',"duration":'+validateDuration(duration)+',"origin":"'+window.webOrigin+'"');
} }

View File

@ -25,7 +25,6 @@ $(document).ready( function() {
audio: false audio: false
}; };
async function startCapture() { async function startCapture() {
streamActive = true; streamActive = true;
@ -77,6 +76,7 @@ $(document).ready( function() {
tracks.forEach(track => track.stop()); tracks.forEach(track => track.stop());
videoElem.srcObject = null; videoElem.srcObject = null;
} }
requestPriorityClear(1);
} }
function takePicture() { function takePicture() {
@ -86,7 +86,7 @@ $(document).ready( function() {
context.drawImage(videoElem, 0, 0, streamImageWidth, streamImageHeight); context.drawImage(videoElem, 0, 0, streamImageWidth, streamImageHeight);
var data = canvasElem.toDataURL('image/png').split(",")[1]; var data = canvasElem.toDataURL('image/png').split(",")[1];
requestSetImage(data, 2, "Streaming"); requestSetImage(data, -1, "Streaming");
} }
// start or update screenshot timer // start or update screenshot timer
@ -108,4 +108,10 @@ $(document).ready( function() {
} }
}); });
$(window.hyperion).on("stopBrowerScreenCapture", function (event) {
if (streamActive) {
stopCapture();
}
});
}); });

View File

@ -304,7 +304,7 @@ function performAction() {
h += '<button id="wiz_cc_btn_sp" class="btn btn-primary">' + $.i18n('wiz_cc_btn_switchpic') + '</button>'; h += '<button id="wiz_cc_btn_sp" class="btn btn-primary">' + $.i18n('wiz_cc_btn_switchpic') + '</button>';
} }
else else
h += '<p>' + $.i18n('wiz_cc_lettvshowm', "gey_1, grey_2, grey_3, HGradient, VGradient") + '</p>'; h += '<p>' + $.i18n('wiz_cc_lettvshowm', "grey_1, grey_2, grey_3, HGradient, VGradient") + '</p>';
$('#wiz_cc_desc').html(h); $('#wiz_cc_desc').html(h);
$('#wiz_cc_btn_sp').off().on('click', function () { $('#wiz_cc_btn_sp').off().on('click', function () {
switchPicture(["VGradient", "grey_1", "grey_2", "grey_3", "HGradient"]); switchPicture(["VGradient", "grey_1", "grey_2", "grey_3", "HGradient"]);
@ -1534,7 +1534,7 @@ async function discover_yeelight_lights() {
function assign_yeelight_lights() { function assign_yeelight_lights() {
// Model mappings, see https://www.home-assistant.io/integrations/yeelight/ // 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 records are left for configuration
if (Object.keys(lights).length > 0) { if (Object.keys(lights).length > 0) {

View File

@ -231,11 +231,11 @@
"pbrv" : 100 "pbrv" : 100
}, },
"matrix": "matrix": {
{
"ledshoriz": 1, "ledshoriz": 1,
"ledsvert": 1, "ledsvert": 1,
"cabling": "snake", "cabling": "snake",
"direction": "horizontal",
"start": "top-left" "start": "top-left"
} }
}, },

View File

@ -336,9 +336,9 @@ protected:
bool getUserToken(QString &userToken); 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 /// @param token The user Token
/// @return True on succes /// @return True on success
/// ///
bool isTokenAuthorized(const QString &token); bool isTokenAuthorized(const QString &token);

View File

@ -17,6 +17,8 @@
// AuthManager // AuthManager
#include <hyperion/AuthManager.h> #include <hyperion/AuthManager.h>
#include <hyperion/PriorityMuxer.h>
class Hyperion; class Hyperion;
class ComponentRegister; class ComponentRegister;
class BonjourBrowserWrapper; class BonjourBrowserWrapper;
@ -78,10 +80,13 @@ private slots:
/// ///
void handleBonjourChange(const QMap<QString,BonjourRecord>& bRegisters); void handleBonjourChange(const QMap<QString,BonjourRecord>& bRegisters);
#endif #endif
/// ///
/// @brief handle emits from PriorityMuxer /// @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 /// @brief Handle imageToLedsMapping updates

View File

@ -58,7 +58,7 @@ private slots:
/// @brief Handle priority updates from Priority Muxer /// @brief Handle priority updates from Priority Muxer
/// @param priority The new visible priority /// @param priority The new visible priority
/// ///
void handlePriorityChanges(quint8 priority); void handlePriorityChanges(int priority);
/// ///
/// @brief Forward message to all json target hosts /// @brief Forward message to all json target hosts

View File

@ -20,14 +20,15 @@ public:
, _prioMuxer(_hyperion->getMuxerInstance()) , _prioMuxer(_hyperion->getMuxerInstance())
, _isBgEffectConfigured(false) , _isBgEffectConfigured(false)
{ {
// listen for config changes
connect(_hyperion, &Hyperion::settingsChanged,
[=](settings::type type, const QJsonDocument& config) { this->handleSettingsUpdate(type, config); }
);
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, // listen for config changes
[=]() { this->handlePriorityUpdate(); } connect(_hyperion, &Hyperion::settingsChanged, this, [=] (settings::type type, const QJsonDocument& config) {
); this->handleSettingsUpdate(type, config);
});
connect(_prioMuxer, &PriorityMuxer::prioritiesChanged, this, [=] {
this->handlePriorityUpdate();
});
// initialization // initialization
handleSettingsUpdate(settings::BGEFFECT, _hyperion->getSetting(settings::BGEFFECT)); handleSettingsUpdate(settings::BGEFFECT, _hyperion->getSetting(settings::BGEFFECT));
@ -49,7 +50,10 @@ private slots:
const QJsonObject& BGEffectConfig = _bgEffectConfig.object(); const QJsonObject& BGEffectConfig = _bgEffectConfig.object();
#define BGCONFIG_ARRAY bgColorConfig.toArray() #define BGCONFIG_ARRAY bgColorConfig.toArray()
// clear background priority // clear background priority
if (_hyperion->getCurrentPriority() == PriorityMuxer::BG_PRIORITY)
{
_hyperion->clear(PriorityMuxer::BG_PRIORITY); _hyperion->clear(PriorityMuxer::BG_PRIORITY);
}
// initial background effect/color // initial background effect/color
if (BGEffectConfig["enable"].toBool(true)) if (BGEffectConfig["enable"].toBool(true))
{ {
@ -92,13 +96,14 @@ private slots:
/// ///
void handlePriorityUpdate() 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"); Debug(Logger::getInstance("HYPERION"),"Stop background (color-) effect as it moved out of scope");
_hyperion->clear(PriorityMuxer::BG_PRIORITY); _hyperion->clear(PriorityMuxer::BG_PRIORITY);
} }
else if (_prioMuxer->getCurrentPriority() == PriorityMuxer::LOWEST_PRIORITY && _isBgEffectConfigured) 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); emit handleSettingsUpdate (settings::BGEFFECT, _bgEffectConfig);
} }
} }

View File

@ -500,7 +500,7 @@ private slots:
/// @brief Handle the scenario when no/an input source is available /// @brief Handle the scenario when no/an input source is available
/// @param priority Current priority /// @param priority Current priority
/// ///
void handleSourceAvailability(const quint8& priority); void handleSourceAvailability(int priority);
private: private:
friend class HyperionDaemon; friend class HyperionDaemon;

View File

@ -54,6 +54,8 @@ public:
QString owner; QString owner;
}; };
typedef QMap<int, InputInfo> InputsMap;
//Foreground and Background priorities //Foreground and Background priorities
const static int FG_PRIORITY; const static int FG_PRIORITY;
const static int BG_PRIORITY; const static int BG_PRIORITY;
@ -62,6 +64,7 @@ public:
const static int LOWEST_PRIORITY; const static int LOWEST_PRIORITY;
/// Timeout used to identify a non active priority /// Timeout used to identify a non active priority
const static int TIMEOUT_NOT_ACTIVE_PRIO; const static int TIMEOUT_NOT_ACTIVE_PRIO;
const static int REMOVE_CLEARED_PRIO;
const static int ENDLESS; const static int ENDLESS;
@ -197,22 +200,13 @@ public:
/// ///
void clearAll(bool forceClearAll=false); 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: 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 /// @brief Emits whenever the visible priority has changed
/// @param priority The new visible priority /// @param priority The new visible priority
/// ///
void visiblePriorityChanged(quint8 priority); void visiblePriorityChanged(int priority);
/// ///
/// @brief Emits whenever the current visible component changed /// @brief Emits whenever the current visible component changed
@ -222,9 +216,13 @@ signals:
/// ///
/// @brief Emits whenever something changes which influences the priorities listing /// @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 /// internal used signal to resolve treading issues with timer
@ -233,15 +231,15 @@ signals:
private slots: 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(); void timeTrigger();
/// ///
/// Updates the current time. Channels with a configured time out will be checked and cleared if /// Updates the current priorities. Channels with a configured time out will be checked and cleared if
/// required. /// required. Cleared priorities will be removed.
/// ///
void setCurrentTime(); void updatePriorities();
private: private:
/// ///
@ -266,7 +264,7 @@ private:
hyperion::Components _prevVisComp = hyperion::COMP_INVALID; hyperion::Components _prevVisComp = hyperion::COMP_INVALID;
/// The mapping from priority channel to led-information /// The mapping from priority channel to led-information
QMap<int, InputInfo> _activeInputs; InputsMap _activeInputs;
/// The information of the lowest priority channel /// The information of the lowest priority channel
InputInfo _lowestPriorityInfo; InputInfo _lowestPriorityInfo;

View File

@ -29,7 +29,7 @@ namespace NetUtils {
server.close(); server.close();
if(port != prevPort) 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 false;
} }
return true; return true;

View File

@ -10,6 +10,7 @@
#include <QDateTime> #include <QDateTime>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QImage> #include <QImage>
#include <QImageReader>
#include <QBuffer> #include <QBuffer>
#include <QByteArray> #include <QByteArray>
#include <QTimer> #include <QTimer>
@ -83,7 +84,9 @@ void API::init()
} }
// if this is localConnection and network allows unauth locals, set authorized flag // if this is localConnection and network allows unauth locals, set authorized flag
if (apiAuthRequired && _localConnection) if (apiAuthRequired && _localConnection)
{
_authorized = !_authManager->isLocalAuthRequired(); _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 // 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) if (_localConnection)
@ -91,9 +94,11 @@ void API::init()
_adminAuthorized = !_authManager->isLocalAdminAuthRequired(); _adminAuthorized = !_authManager->isLocalAdminAuthRequired();
// just in positive direction // just in positive direction
if (_adminAuthorized) if (_adminAuthorized)
{
_authorized = true; _authorized = true;
} }
} }
}
void API::setColor(int priority, const std::vector<uint8_t> &ledColors, int timeout_ms, const QString &origin, hyperion::Components callerComp) void API::setColor(int priority, const std::vector<uint8_t> &ledColors, int timeout_ms, const QString &origin, hyperion::Components callerComp)
{ {
@ -113,12 +118,25 @@ bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &reply
// truncate name length // truncate name length
data.imgName.truncate(16); data.imgName.truncate(16);
if (!data.format.isEmpty())
{
if (data.format == "auto") if (data.format == "auto")
{ {
QImage img = QImage::fromData(data.data); 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()) 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; return false;
} }

View File

@ -365,20 +365,26 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
QJsonArray priorities; QJsonArray priorities;
uint64_t now = QDateTime::currentMSecsSinceEpoch(); uint64_t now = QDateTime::currentMSecsSinceEpoch();
QList<int> activePriorities = _hyperion->getActivePriorities(); QList<int> activePriorities = _hyperion->getActivePriorities();
activePriorities.removeAll(255); activePriorities.removeAll(PriorityMuxer::LOWEST_PRIORITY);
int currentPriority = _hyperion->getCurrentPriority(); int currentPriority = _hyperion->getCurrentPriority();
for(int priority : activePriorities) for(int priority : qAsConst(activePriorities))
{ {
const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(priority); const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(priority);
QJsonObject item; QJsonObject item;
item["priority"] = priority; item["priority"] = priority;
if (priorityInfo.timeoutTime_ms > 0 ) if (priorityInfo.timeoutTime_ms > 0 )
{
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now); item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
}
// owner has optional informations to the component // owner has optional informations to the component
if (!priorityInfo.owner.isEmpty()) if (!priorityInfo.owner.isEmpty())
{
item["owner"] = priorityInfo.owner; item["owner"] = priorityInfo.owner;
}
item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId));
item["origin"] = priorityInfo.origin; item["origin"] = priorityInfo.origin;
@ -397,7 +403,8 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
LEDcolor.insert("RGB", RGBValue); LEDcolor.insert("RGB", RGBValue);
uint16_t Hue; uint16_t Hue;
float Saturation, Luminace; float Saturation;
float Luminace;
// add HSL Value to Array // add HSL Value to Array
QJsonArray HSLValue; QJsonArray HSLValue;

View File

@ -44,6 +44,8 @@ JsonCB::JsonCB(QObject* parent)
#if defined(ENABLE_EFFECTENGINE) #if defined(ENABLE_EFFECTENGINE)
_availableCommands << "effects-update"; _availableCommands << "effects-update";
#endif #endif
qRegisterMetaType<PriorityMuxer::InputsMap>("InputsMap");
} }
bool JsonCB::subscribeFor(const QString& type, bool unsubscribe) bool JsonCB::subscribeFor(const QString& type, bool unsubscribe)
@ -226,25 +228,33 @@ void JsonCB::handleBonjourChange(const QMap<QString,BonjourRecord>& bRegisters)
doCallback("sessions-update", QVariant(data)); doCallback("sessions-update", QVariant(data));
} }
#endif #endif
void JsonCB::handlePriorityUpdate()
void JsonCB::handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs)
{ {
QJsonObject data; QJsonObject data;
QJsonArray priorities; QJsonArray priorities;
uint64_t now = QDateTime::currentMSecsSinceEpoch(); uint64_t now = QDateTime::currentMSecsSinceEpoch();
QList<int> activePriorities = _prioMuxer->getPriorities(); QList<int> activePriorities = activeInputs.keys();
activePriorities.removeAll(255);
int currentPriority = _prioMuxer->getCurrentPriority(); 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; QJsonObject item;
item["priority"] = priority; item["priority"] = priority;
if (priorityInfo.timeoutTime_ms > 0 ) if (priorityInfo.timeoutTime_ms > 0 )
{
item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now); item["duration_ms"] = int(priorityInfo.timeoutTime_ms - now);
}
// owner has optional informations to the component // owner has optional informations to the component
if(!priorityInfo.owner.isEmpty()) if(!priorityInfo.owner.isEmpty())
{
item["owner"] = priorityInfo.owner; item["owner"] = priorityInfo.owner;
}
item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId)); item["componentId"] = QString(hyperion::componentToIdString(priorityInfo.componentId));
item["origin"] = priorityInfo.origin; item["origin"] = priorityInfo.origin;
@ -263,7 +273,8 @@ void JsonCB::handlePriorityUpdate()
LEDcolor.insert("RGB", RGBValue); LEDcolor.insert("RGB", RGBValue);
uint16_t Hue; uint16_t Hue;
float Saturation, Luminace; float Saturation;
float Luminace;
// add HSL Value to Array // add HSL Value to Array
QJsonArray HSLValue; QJsonArray HSLValue;

View File

@ -55,11 +55,7 @@ BoblightClientConnection::BoblightClientConnection(Hyperion* hyperion, QTcpSocke
BoblightClientConnection::~BoblightClientConnection() BoblightClientConnection::~BoblightClientConnection()
{ {
// clear the current channel _socket->deleteLater();
if (_priority != 0 && _priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY)
_hyperion->clear(_priority);
delete _socket;
} }
void BoblightClientConnection::readData() void BoblightClientConnection::readData()
@ -117,9 +113,10 @@ QString BoblightClientConnection::readMessage(const char* data, const size_t siz
void BoblightClientConnection::socketClosed() void BoblightClientConnection::socketClosed()
{ {
// clear the current channel
if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY)
{
_hyperion->clear(_priority); _hyperion->clear(_priority);
}
emit connectionClosed(this); emit connectionClosed(this);
} }
@ -205,11 +202,17 @@ void BoblightClientConnection::handleMessage(const QString& message)
{ {
bool rc; bool rc;
const int prio = static_cast<int>(parseUInt(messageParts[2], &rc)); const int prio = static_cast<int>(parseUInt(messageParts[2], &rc));
if (rc && prio != _priority) if (rc)
{ {
if (_priority != 0 && _hyperion->getPriorityInfo(_priority).componentId == hyperion::COMP_BOBLIGHTSERVER) int currentPriority = _hyperion->getCurrentPriority();
_hyperion->clear(_priority);
if (prio == currentPriority)
{
Error(_log, "The priority %i is already in use onther component of type [%s]", prio, componentToString(_hyperion->getPriorityInfo(currentPriority).componentId));
_socket->close();
}
else
{
if (prio < BOBLIGHT_MIN_PRIORITY || prio > BOBLIGHT_MAX_PRIORITY) if (prio < BOBLIGHT_MIN_PRIORITY || prio > BOBLIGHT_MAX_PRIORITY)
{ {
_priority = BOBLIGHT_DEFAULT_PRIORITY; _priority = BOBLIGHT_DEFAULT_PRIORITY;
@ -222,23 +225,35 @@ 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.", 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); prio, BOBLIGHT_MIN_PRIORITY, BOBLIGHT_MAX_PRIORITY, _priority);
// register new priority (previously modified) // register new priority (previously modified)
_hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_socket->peerAddress().toString())); _hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress));
} }
else else
{ {
// register new priority // register new priority
_hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_socket->peerAddress().toString())); _hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress));
_priority = prio; _priority = prio;
} }
return;
} }
} }
return;
}
} }
else if (messageParts[0] == "sync") else if (messageParts[0] == "sync")
{ {
if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY) if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY)
{
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 _hyperion->setInput(_priority, _ledColors); // send current color values to hyperion
}
}
return; return;
} }
@ -387,6 +402,14 @@ uint8_t BoblightClientConnection::parseByte(const QString& s, bool *ok) const
return static_cast<uint8_t>(qBound(LO, int(HI * d), HI)); // qBound args are in order min, value, max; see: https://doc.qt.io/qt-5/qtglobal.html#qBound return static_cast<uint8_t>(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() void BoblightClientConnection::sendLightMessage()
{ {
char buffer[256]; char buffer[256];

View File

@ -36,6 +36,13 @@ public:
/// ///
~BoblightClientConnection() override; ~BoblightClientConnection() override;
///
/// Get the Boblight client's IP-address
///
/// @returns IP-address as QString
///
QString getClientAddress() { return _clientAddress; }
signals: signals:
/// ///
/// Signal which is emitted when the connection is being closed /// Signal which is emitted when the connection is being closed
@ -67,7 +74,7 @@ private:
/// ///
/// @param message The boblight message to send /// @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 /// Send a lights message the to connected client

View File

@ -22,9 +22,12 @@ BoblightServer::BoblightServer(Hyperion* hyperion,const QJsonDocument& config)
, _server(new QTcpServer(this)) , _server(new QTcpServer(this))
, _openConnections() , _openConnections()
, _priority(0) , _priority(0)
, _log(Logger::getInstance("BOBLIGHT")) , _log(nullptr)
, _port(0) , _port(0)
{ {
QString subComponent = _hyperion->property("instance").toString();
_log= Logger::getInstance("BOBLIGHT", subComponent);
Debug(_log, "Instance created"); Debug(_log, "Instance created");
// listen for component change // listen for component change
@ -49,7 +52,7 @@ void BoblightServer::start()
if (NetUtils::portAvailable(_port, _log)) if (NetUtils::portAvailable(_port, _log))
_server->listen(QHostAddress::Any, _port); _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()); _hyperion->setNewComponentState(COMP_BOBLIGHTSERVER, _server->isListening());
} }
@ -92,11 +95,9 @@ uint16_t BoblightServer::getPort() const
void BoblightServer::newConnection() void BoblightServer::newConnection()
{ {
QTcpSocket * socket = _server->nextPendingConnection(); QTcpSocket * socket = _server->nextPendingConnection();
if (socket != nullptr) if (socket != nullptr)
{ {
Info(_log, "new connection"); Info(_log, "New connection from %s ", QSTRING_CSTR(QString("Boblight@%1").arg(socket->peerAddress().toString())));
_hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(socket->peerAddress().toString()));
BoblightClientConnection * connection = new BoblightClientConnection(_hyperion, socket, _priority); BoblightClientConnection * connection = new BoblightClientConnection(_hyperion, socket, _priority);
_openConnections.insert(connection); _openConnections.insert(connection);
@ -107,7 +108,7 @@ void BoblightServer::newConnection()
void BoblightServer::closedConnection(BoblightClientConnection *connection) 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); _openConnections.remove(connection);
// schedule to delete the connection object // schedule to delete the connection object

View File

@ -219,7 +219,7 @@ void EffectEngine::effectFinished()
_hyperion->clear(effect->getPriority()); _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) for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt)
{ {
if (*effectIt == effect) if (*effectIt == effect)

View File

@ -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(); const QJsonObject obj = _hyperion->getSetting(settings::NETFORWARD).object();
if (priority != 0 && _forwarder_enabled && obj["enable"].toBool()) if (priority != 0 && _forwarder_enabled && obj["enable"].toBool())

View File

@ -106,14 +106,15 @@ bool QtGrabber::setupDisplay()
for (auto* screen : qAsConst(screens)) for (auto* screen : qAsConst(screens))
{ {
const QRect geo = screen->geometry(); 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; ++index;
} }
if (screens.at(0)->size() != screens.at(0)->virtualSize()) if (screens.at(0)->size() != screens.at(0)->virtualSize())
{ {
const QRect vgeo = screens.at(0)->virtualGeometry(); 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; _isVirtual = false;
@ -155,7 +156,7 @@ bool QtGrabber::setupDisplay()
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); 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 QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int height) const
{ {
QSize windowSize; QSize windowSize;
int x = xIn;
int y = yIn;
HWND hwnd = reinterpret_cast<HWND>(window); HWND hwnd = reinterpret_cast<HWND>(window);
if (hwnd) if (hwnd)
{ {
@ -179,15 +178,13 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int
hwnd = GetDesktopWindow(); hwnd = GetDesktopWindow();
const QRect screenGeometry = _screen->geometry(); const QRect screenGeometry = _screen->geometry();
windowSize = screenGeometry.size(); windowSize = screenGeometry.size();
x += screenGeometry.x();
y += screenGeometry.y();
} }
if (width < 0) if (width < 0)
width = windowSize.width() - x; width = windowSize.width() - xIn;
if (height < 0) if (height < 0)
height = windowSize.height() - y; height = windowSize.height() - yIn;
// Create and setup bitmap // Create and setup bitmap
HDC display_dc = GetDC(nullptr); HDC display_dc = GetDC(nullptr);
@ -197,7 +194,7 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int
// copy data // copy data
HDC window_dc = GetDC(hwnd); 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 // clean up all but bitmap
ReleaseDC(hwnd, window_dc); ReleaseDC(hwnd, window_dc);
@ -288,23 +285,33 @@ int QtGrabber::updateScreenDimensions(bool force)
? ((_height - _cropTop - _cropBottom) / _pixelDecimation) ? ((_height - _cropTop - _cropBottom) / _pixelDecimation)
: (_height / _pixelDecimation); : (_height / _pixelDecimation);
// calculate final image dimensions and adjust top/left cropping in 3D modes // 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) switch (_videoMode)
{ {
case VideoMode::VIDEO_3DSBS: case VideoMode::VIDEO_3DSBS:
_calculatedWidth = width / 2; _calculatedWidth = width / 2;
_calculatedHeight = height; _calculatedHeight = height;
_src_x = _cropLeft / 2; _src_x = _src_x + (_cropLeft / 2);
_src_y = _cropTop; _src_y = _src_y + _cropTop;
_src_x_max = (_width / 2) - _cropRight - _cropLeft; _src_x_max = (_width / 2) - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop; _src_y_max = _height - _cropBottom - _cropTop;
break; break;
case VideoMode::VIDEO_3DTAB: case VideoMode::VIDEO_3DTAB:
_calculatedWidth = width; _calculatedWidth = width;
_calculatedHeight = height / 2; _calculatedHeight = height / 2;
_src_x = _cropLeft; _src_x = _src_x + _cropLeft;
_src_y = _cropTop / 2; _src_y = _src_y + (_cropTop / 2);
_src_x_max = _width - _cropRight - _cropLeft; _src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = (_height / 2) - _cropBottom - _cropTop; _src_y_max = (_height / 2) - _cropBottom - _cropTop;
break; break;
@ -312,14 +319,16 @@ int QtGrabber::updateScreenDimensions(bool force)
default: default:
_calculatedWidth = width; _calculatedWidth = width;
_calculatedHeight = height; _calculatedHeight = height;
_src_x = _cropLeft; _src_x = _src_x + _cropLeft;
_src_y = _cropTop; _src_y = _src_y + _cropTop;
_src_x_max = _width - _cropRight - _cropLeft; _src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop; _src_y_max = _height - _cropBottom - _cropTop;
break; break;
} }
Info(_log, "Update output image resolution to [%dx%d]", _calculatedWidth, _calculatedHeight); 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; return 1;
} }
@ -351,13 +360,19 @@ void QtGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBo
bool QtGrabber::setDisplayIndex(int index) bool QtGrabber::setDisplayIndex(int index)
{ {
bool rc(true); bool rc(true);
if (_display != index) if (_display != index || _isVirtual)
{ {
_isVirtual = false;
if (index <= _numberOfSDisplays) if (index <= _numberOfSDisplays)
{ {
_display = index; _display = index;
if (index == _numberOfSDisplays)
{
_isVirtual = true;
} }
else { }
else
{
_display = 0; _display = 0;
} }
rc = setupDisplay(); rc = setupDisplay();
@ -460,5 +475,4 @@ QJsonObject QtGrabber::discover(const QJsonObject& params)
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData()); DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered; return inputsDiscovered;
} }

View File

@ -193,7 +193,10 @@ void Hyperion::stop()
void Hyperion::freeObjects() 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); clear(-1,true);
// delete components on exit of hyperion core // delete components on exit of hyperion core
@ -462,11 +465,6 @@ void Hyperion::setColor(int priority, const std::vector<ColorRgb> &ledColors, in
} }
end: end:
if (getPriorityInfo(priority).componentId != hyperion::COMP_COLOR)
{
clear(priority);
}
// register color // register color
registerInput(priority, hyperion::COMP_COLOR, origin); 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)); _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(); { int previousPriority = _muxer->getPreviousPriority();
Debug(_log,"priority[%d], previousPriority[%d]", priority, previousPriority);
if ( priority == PriorityMuxer::LOWEST_PRIORITY) if ( priority == PriorityMuxer::LOWEST_PRIORITY)
{ {
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");

View File

@ -17,6 +17,7 @@ const int PriorityMuxer::BG_PRIORITY = 254;
const int PriorityMuxer::MANUAL_SELECTED_PRIORITY = 256; const int PriorityMuxer::MANUAL_SELECTED_PRIORITY = 256;
const int PriorityMuxer::LOWEST_PRIORITY = std::numeric_limits<uint8_t>::max(); const int PriorityMuxer::LOWEST_PRIORITY = std::numeric_limits<uint8_t>::max();
const int PriorityMuxer::TIMEOUT_NOT_ACTIVE_PRIO = -100; const int PriorityMuxer::TIMEOUT_NOT_ACTIVE_PRIO = -100;
const int PriorityMuxer::REMOVE_CLEARED_PRIO = -101;
const int PriorityMuxer::ENDLESS = -1; const int PriorityMuxer::ENDLESS = -1;
PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent) PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent)
@ -26,8 +27,6 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent)
, _previousPriority(_currentPriority) , _previousPriority(_currentPriority)
, _manualSelectedPriority(MANUAL_SELECTED_PRIORITY) , _manualSelectedPriority(MANUAL_SELECTED_PRIORITY)
, _prevVisComp (hyperion::Components::COMP_COLOR) , _prevVisComp (hyperion::Components::COMP_COLOR)
, _activeInputs()
, _lowestPriorityInfo()
, _sourceAutoSelectEnabled(true) , _sourceAutoSelectEnabled(true)
, _updateTimer(new QTimer(this)) , _updateTimer(new QTimer(this))
, _timer(new QTimer(this)) , _timer(new QTimer(this))
@ -38,8 +37,10 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent)
// init lowest priority info // init lowest priority info
_lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY; _lowestPriorityInfo.priority = PriorityMuxer::LOWEST_PRIORITY;
_lowestPriorityInfo.timeoutTime_ms = PriorityMuxer::ENDLESS;
_lowestPriorityInfo.ledColors = std::vector<ColorRgb>(ledCount, {0, 0, 0}); _lowestPriorityInfo.timeoutTime_ms = -1;
_lowestPriorityInfo.ledColors = std::vector<ColorRgb>(ledCount, ColorRgb::BLACK);
_lowestPriorityInfo.componentId = hyperion::COMP_COLOR; _lowestPriorityInfo.componentId = hyperion::COMP_COLOR;
_lowestPriorityInfo.origin = "System"; _lowestPriorityInfo.origin = "System";
_lowestPriorityInfo.owner = ""; _lowestPriorityInfo.owner = "";
@ -50,12 +51,10 @@ PriorityMuxer::PriorityMuxer(int ledCount, QObject * parent)
connect(_timer, &QTimer::timeout, this, &PriorityMuxer::timeTrigger); connect(_timer, &QTimer::timeout, this, &PriorityMuxer::timeTrigger);
_timer->setSingleShot(true); _timer->setSingleShot(true);
_blockTimer->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); connect(this, &PriorityMuxer::signalTimeTrigger, this, &PriorityMuxer::timeTrigger);
// start muxer timer // start muxer timer
connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::setCurrentTime); connect(_updateTimer, &QTimer::timeout, this, &PriorityMuxer::updatePriorities);
_updateTimer->setInterval(250); _updateTimer->setInterval(250);
_updateTimer->start(); _updateTimer->start();
} }
@ -85,7 +84,9 @@ bool PriorityMuxer::setSourceAutoSelectEnabled(bool enable, bool update)
// update _currentPriority if called from external // update _currentPriority if called from external
if(update) if(update)
setCurrentTime(); {
emit prioritiesChanged(_currentPriority,_activeInputs);
}
return true; return true;
} }
@ -128,10 +129,10 @@ bool PriorityMuxer::hasPriority(int priority) const
PriorityMuxer::InputInfo PriorityMuxer::getInputInfo(int priority) const PriorityMuxer::InputInfo PriorityMuxer::getInputInfo(int priority) const
{ {
auto elemIt = _activeInputs.find(priority); auto elemIt = _activeInputs.constFind(priority);
if (elemIt == _activeInputs.end()) if (elemIt == _activeInputs.end())
{ {
elemIt = _activeInputs.find(PriorityMuxer::LOWEST_PRIORITY); elemIt = _activeInputs.constFind(PriorityMuxer::LOWEST_PRIORITY);
if (elemIt == _activeInputs.end()) if (elemIt == _activeInputs.end())
{ {
// fallback // fallback
@ -150,11 +151,18 @@ void PriorityMuxer::registerInput(int priority, hyperion::Components component,
{ {
// detect new registers // detect new registers
bool newInput = false; bool newInput = false;
bool reusedInput = false;
if (!_activeInputs.contains(priority)) if (!_activeInputs.contains(priority))
{
newInput = true; newInput = true;
}
else if(_prevVisComp == component || _activeInputs[priority].componentId == component) else if(_prevVisComp == component || _activeInputs[priority].componentId == component)
reusedInput = true; {
if (_activeInputs[priority].owner != owner)
{
newInput = true;
}
}
InputInfo& input = _activeInputs[priority]; InputInfo& input = _activeInputs[priority];
input.priority = priority; input.priority = priority;
@ -166,18 +174,11 @@ void PriorityMuxer::registerInput(int priority, hyperion::Components component,
if (newInput) if (newInput)
{ {
Debug(_log,"Register new input '%s/%s' with priority %d as inactive", QSTRING_CSTR(origin), hyperion::componentToIdString(component), priority); Debug(_log,"Register new input '%s/%s' (%s) with priority %d as inactive", QSTRING_CSTR(origin), hyperion::componentToIdString(component), QSTRING_CSTR(owner), priority);
// emit 'prioritiesChanged' only if _sourceAutoSelectEnabled is false
if (!_sourceAutoSelectEnabled)
{
emit prioritiesChanged();
} }
return; else
}
if (reusedInput)
{ {
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<ColorRgb>& ledColor
// emit active change // emit active change
if(activeChange) if(activeChange)
{ {
Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); if (_currentPriority <= priority || !_sourceAutoSelectEnabled)
if (_currentPriority < priority)
{ {
emit prioritiesChanged(); Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive");
emit prioritiesChanged(_currentPriority,_activeInputs);
} }
setCurrentTime(); updatePriorities();
} }
return true; return true;
@ -272,12 +273,12 @@ bool PriorityMuxer::setInputImage(int priority, const Image<ColorRgb>& image, in
// emit active change // emit active change
if(activeChange) if(activeChange)
{ {
Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive"); if (_currentPriority <= priority || !_sourceAutoSelectEnabled)
if (_currentPriority < priority)
{ {
emit prioritiesChanged(); Debug(_log, "Priority %d is now %s", priority, active ? "active" : "inactive");
emit prioritiesChanged(_currentPriority,_activeInputs);
} }
setCurrentTime(); updatePriorities();
} }
return true; return true;
@ -291,14 +292,9 @@ bool PriorityMuxer::setInputInactive(int priority)
bool PriorityMuxer::clearInput(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); _activeInputs[priority].timeoutTime_ms = REMOVE_CLEARED_PRIO;
// on clear success update _currentPriority
setCurrentTime();
// emit 'prioritiesChanged' only if _sourceAutoSelectEnabled is false
if ((!_sourceAutoSelectEnabled && (_currentPriority < priority)) || _currentPriority == BG_PRIORITY)
emit prioritiesChanged();
return true; return true;
} }
return false; return false;
@ -312,6 +308,7 @@ void PriorityMuxer::clearAll(bool forceClearAll)
_activeInputs.clear(); _activeInputs.clear();
_currentPriority = PriorityMuxer::LOWEST_PRIORITY; _currentPriority = PriorityMuxer::LOWEST_PRIORITY;
_activeInputs[_currentPriority] = _lowestPriorityInfo; _activeInputs[_currentPriority] = _lowestPriorityInfo;
updatePriorities();
} }
else else
{ {
@ -326,35 +323,61 @@ void PriorityMuxer::clearAll(bool forceClearAll)
} }
} }
void PriorityMuxer::setCurrentTime() void PriorityMuxer::updatePriorities()
{ {
const int64_t now = QDateTime::currentMSecsSinceEpoch(); const int64_t now = QDateTime::currentMSecsSinceEpoch();
int newPriority; int newPriority;
bool priorityChanged {false};
_activeInputs.contains(0) ? newPriority = 0 : newPriority = PriorityMuxer::LOWEST_PRIORITY; _activeInputs.contains(0) ? newPriority = 0 : newPriority = PriorityMuxer::LOWEST_PRIORITY;
for (auto infoIt = _activeInputs.begin(); infoIt != _activeInputs.end();) QMutableMapIterator<int, PriorityMuxer::InputInfo> i(_activeInputs);
while (i.hasNext()) {
i.next();
if ( i.value().timeoutTime_ms == REMOVE_CLEARED_PRIO )
{ {
if (infoIt->timeoutTime_ms > 0 && infoIt->timeoutTime_ms <= now) int tPrio = i.value().priority;
i.remove();
Debug(_log,"Removed source priority %d", tPrio);
priorityChanged = true;
}
else
{ {
int tPrio = infoIt->priority; if (i.value().timeoutTime_ms > 0 && i.value().timeoutTime_ms <= now)
infoIt = _activeInputs.erase(infoIt); {
//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); Debug(_log,"Timeout clear for priority %d",tPrio);
emit prioritiesChanged(); priorityChanged = true;
} }
else else
{ {
// timeoutTime of TIMEOUT_NOT_ACTIVE_PRIO is awaiting data (inactive); skip // timeoutTime of TIMEOUT_NOT_ACTIVE_PRIO is awaiting data (inactive); skip
if(infoIt->timeoutTime_ms > TIMEOUT_NOT_ACTIVE_PRIO) if(i.value().timeoutTime_ms > TIMEOUT_NOT_ACTIVE_PRIO)
newPriority = qMin(newPriority, infoIt->priority); {
newPriority = qMin(newPriority, i.value().priority);
}
// call timeTrigger when effect or color is running with timeout > 0, blacklist prio 255 // 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().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 emit signalTimeTrigger(); // as signal to prevent Threading issues
} }
++infoIt;
} }
} }
}
// evaluate, if manual selected priority is still available // evaluate, if manual selected priority is still available
if(!_sourceAutoSelectEnabled) if(!_sourceAutoSelectEnabled)
{ {
@ -383,7 +406,12 @@ void PriorityMuxer::setCurrentTime()
_prevVisComp = comp; _prevVisComp = comp;
emit visibleComponentChanged(comp); emit visibleComponentChanged(comp);
} }
emit prioritiesChanged(); priorityChanged = true;
}
if (priorityChanged)
{
emit prioritiesChanged(_currentPriority,_activeInputs);
} }
} }
@ -395,7 +423,7 @@ void PriorityMuxer::timeTrigger()
} }
else else
{ {
emit timeRunner();
_blockTimer->start(1000); _blockTimer->start(1000);
emit prioritiesChanged(_currentPriority,_activeInputs);
} }
} }

View File

@ -15,6 +15,7 @@
"type" : "integer", "type" : "integer",
"required" : true, "required" : true,
"title" : "edt_conf_general_port_title", "title" : "edt_conf_general_port_title",
"default" : 19333,
"minimum" : 1024, "minimum" : 1024,
"maximum" : 65535, "maximum" : 65535,
"propertyOrder" : 2 "propertyOrder" : 2

View File

@ -137,6 +137,10 @@
"type": "string", "type": "string",
"enum": [ "snake", "parallel" ] "enum": [ "snake", "parallel" ]
}, },
"direction": {
"type": "string",
"enum": [ "horizontal", "vertical" ]
},
"start": { "start": {
"type": "string", "type": "string",
"enum": [ "top-left", "top-right", "bottom-left", "bottom-right" ] "enum": [ "top-left", "top-right", "bottom-left", "bottom-right" ]

View File

@ -357,8 +357,10 @@ QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
} }
QJsonObject propertiesDetails = response.getBody().object(); QJsonObject propertiesDetails = response.getBody().object();
if (!propertiesDetails.isEmpty())
{
propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM); propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM);
}
properties.insert("properties", propertiesDetails); properties.insert("properties", propertiesDetails);
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() ); DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );

View File

@ -101,7 +101,11 @@ void QtHttpClientWrapper::onClientDataReceived (void)
QByteArray header = raw.left (pos).trimmed(); QByteArray header = raw.left (pos).trimmed();
QByteArray value = raw.mid (pos +1).trimmed(); QByteArray value = raw.mid (pos +1).trimmed();
m_currentRequest->addHeader (header, value); 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; bool ok = false;
const int len = value.toInt (&ok, 10); const int len = value.toInt (&ok, 10);
@ -153,7 +157,7 @@ void QtHttpClientWrapper::onClientDataReceived (void)
case RequestParsed: // a valid request has ben fully parsed case RequestParsed: // a valid request has ben fully parsed
{ {
// Catch websocket header "Upgrade" // Catch websocket header "Upgrade"
if(m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower() == "websocket") if(m_currentRequest->getHeader(QtHttpHeader::Upgrade) == "websocket")
{ {
if(m_websocketClient == Q_NULLPTR) if(m_websocketClient == Q_NULLPTR)
{ {
@ -327,7 +331,7 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHtt
{ {
static const QByteArray & CLOSE = QByteArrayLiteral ("close"); 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 // must close connection after this request
m_sockClient->close (); m_sockClient->close ();

View File

@ -25,7 +25,7 @@ void QtHttpRequest::setClientInfo (const QHostAddress & server, const QHostAddre
void QtHttpRequest::addHeader (const QByteArray & header, const QByteArray & value) void QtHttpRequest::addHeader (const QByteArray & header, const QByteArray & value)
{ {
QByteArray key = header.trimmed (); QByteArray key = header.trimmed().toLower();
if (!key.isEmpty ()) if (!key.isEmpty ())
{ {

View File

@ -38,7 +38,7 @@ public:
QByteArray getHeader (const QByteArray & header) const QByteArray getHeader (const QByteArray & header) const
{ {
return m_headersHash.value (header, QByteArray ()); return m_headersHash.value (header.toLower(), QByteArray ());
}; };
public slots: public slots:

View File

@ -68,7 +68,7 @@ void JsonConnection::setColor(std::vector<QColor> colors, int priority, int dura
parseReply(reply); 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()); 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["command"] = QString("image");
command["priority"] = priority; command["priority"] = priority;
command["origin"] = QString("hyperion-remote"); command["origin"] = QString("hyperion-remote");
if (!name.isEmpty())
command["name"] = name;
command["imagewidth"] = image.width(); command["imagewidth"] = image.width();
command["imageheight"] = image.height(); command["imageheight"] = image.height();
command["imagedata"] = QString(base64Image.data()); command["imagedata"] = QString(base64Image.data());

View File

@ -41,13 +41,14 @@ public:
void setColor(std::vector<QColor> color, int priority, int duration); void setColor(std::vector<QColor> 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 image The image
/// @param priority The priority /// @param priority The priority
/// @param duration The duration in milliseconds /// @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) #if defined(ENABLE_EFFECTENGINE)
/// ///

View File

@ -252,7 +252,8 @@ int main(int argc, char * argv[])
} }
else if (parser.isSet(argImage)) 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) #if defined(ENABLE_EFFECTENGINE)
else if (parser.isSet(argEffect)) else if (parser.isSet(argEffect))

View File

@ -103,23 +103,7 @@ QCoreApplication* createApplication(int &argc, char *argv[])
#else #else
if (!forceNoGui) if (!forceNoGui)
{ {
// if x11, then test if xserver is available isGuiApp = (getenv("DISPLAY") != NULL && (getenv("XDG_SESSION_TYPE") != NULL || getenv("WAYLAND_DISPLAY") != NULL));
#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
} }
#endif #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); Debug(log,"QtVersion [%s]", QT_VERSION_STR);
if ( !readonlyMode ) if ( !readonlyMode )
@ -423,7 +407,7 @@ int main(int argc, char** argv)
// run the application // run the application
if (isGuiApp) if (isGuiApp)
{ {
Info(log, "start systray"); Info(log, "Start Systray menu");
QApplication::setQuitOnLastWindowClosed(false); QApplication::setQuitOnLastWindowClosed(false);
SysTray tray(hyperiond); SysTray tray(hyperiond);
tray.hide(); tray.hide();