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"
mkdir build || exit 1
cd build
cmake -G "Visual Studio 16 2019" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../ || exit 2
cmake -G "Visual Studio 17 2022" -A x64 -DPLATFORM=${PLATFORM} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ../ || exit 2
cmake --build . --target package --config Release -- -nologo -v:m -maxcpucount || exit 3
exit 0;
exit 1 || { echo "---> Hyperion compilation failed! Abort"; exit 5; }

View File

@ -121,10 +121,10 @@ jobs:
windows:
name: Windows
runs-on: windows-latest
runs-on: windows-2022
env:
VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC'
QT_VERSION: 5.15.0
VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC'
QT_VERSION: 5.15.2
steps:
- name: Checkout
uses: actions/checkout@v1
@ -159,12 +159,6 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
key: ${{ runner.os }}-chocolatey
- name: "Remove Redistributable"
shell: cmd
run: |
MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}
MsiExec.exe /passive /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7}
- name: Install Python, NSIS, OpenSSL, DirectX SDK
shell: powershell
run: |

View File

@ -91,10 +91,10 @@ jobs:
windows:
name: Windows
runs-on: windows-latest
runs-on: windows-2022
env:
VCINSTALLDIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC'
QT_VERSION: 5.15.0
VCINSTALLDIR: 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC'
QT_VERSION: 5.15.2
steps:
- name: Checkout
uses: actions/checkout@v1
@ -122,12 +122,6 @@ jobs:
path: C:\Users\runneradmin\AppData\Local\Temp\chocolatey
key: ${{ runner.os }}-chocolatey
- name: "Remove Redistributable"
shell: cmd
run: |
MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}
MsiExec.exe /passive /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7}
- name: Install Python, NSIS, OpenSSL, DirectX SDK
shell: powershell
run: |

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 restart Hyperion via Systray
- LED Matrix Layout - Support vertical cabling direction (#1420)
- Support additional Yeelight models
- LED-Devices: Show warning, if get properties failed (Network devices: indication that network device is not reachable)
- hyperion-remote: Show image filename in UI for images sent
### Changed
- Colors Smoothing is started in pause mode to save resources, when Hyperion starts with no active source
- Boblight: Support multiple Boblight clients with different priorities
- UI: Allow configuration of a Boblight server per LED-instance
- UI: LED Layout - Removed limitations on indention
### Fixed
- Effects: Fix image URL in Matrix effect
- Effects: Fix that start effect is stuck on UI
- Fixes that the Led-Device output flow was interrupted, by an enabling API request on an already enabled device (#967
- Yeelight - Workaround: Ignore error when setting music mode = off, but the music-mode is already off (#1372)
- Standalone grabbers: Improved fps help/error text, fixed default address and port, fixed auto discovery of Hyperion server in hyperion-remote
- Fixed Qt version override, e.g. set via QTDIR
- Remote control UI: Treat duration=0 as endless
- Stop Web-Browser capture when user triggers other activities
- Treat http headers case insensitive (RFC 2616)
- Qt-Grabber: Fixed position handling of multiple monitors (#1320, #1403)
- Fixed: Signal detection does not switch off all instances (#1281)
- Reworked PriorityMuxer and Sub-scriptions
## Removed

View File

@ -2,7 +2,7 @@
<div class="container-fluid">
<div class="row">
<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-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>
</td>
<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>
</td>
</tr>
<tr>
<td class="ltd"></td>
<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>
</td>
</tr>
@ -215,14 +215,14 @@
<label class="ltdlabel" for="ip_cl_ptr" data-i18n="conf_leds_layout_ptr">Point Top Right</label>
</td>
<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>
</td>
</tr>
<tr>
<td class="ltd"></td>
<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>
</td>
</tr>
@ -231,14 +231,14 @@
<label class="ltdlabel" for="ip_cl_pbr" data-i18n="conf_leds_layout_pbr">Point Bottom Right</label>
</td>
<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>
</td>
</tr>
<tr>
<td class="ltd"></td>
<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>
</td>
</tr>
@ -247,14 +247,14 @@
<label class="ltdlabel" for="ip_cl_pbl" data-i18n="conf_leds_layout_pbl">Point Bottom Left</label>
</td>
<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>
</td>
</tr>
<tr>
<td class="ltd"></td>
<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>
</td>
</tr>
@ -402,3 +402,4 @@
<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>
</td>
</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>
<td></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_device_info_log": "In case your LEDs do not work, check here for errors:",
"conf_leds_device_intro": "Hyperion supports a lot of controllers to transmit data to your target device. Select a LED controller out of the sorted list and configure it. We have chosen the best default settings for each device.",
"conf_leds_error_get_properties_text" : "Failed to get the device's properties. Please check the configuration items.",
"conf_leds_error_get_properties_title" : "Device properties",
"conf_leds_error_hwled_gt_layout": "The hardware LED count ($1) is greater than LEDs configured via layout ($2),<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_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_intro": "You also need an LED layout, which reflects your LED positions. The classic layout is the usually used TV frame, but we also support LED matrix (LED walls) creation. The view on this layout is ALWAYS from the FRONT of your TV.",
"conf_leds_layout_ma_cabling": "Cabling",
"conf_leds_layout_ma_direction": "Direction",
"conf_leds_layout_ma_horiz": "Horizontal",
"conf_leds_layout_ma_optbottomleft": "Bottom left",
"conf_leds_layout_ma_optbottomright": "Bottom right",
@ -185,6 +188,7 @@
"dashboard_infobox_label_instance": "Instance:",
"dashboard_infobox_label_latesthyp": "Latest Hyperion version:",
"dashboard_infobox_label_platform": "Platform:",
"dashboard_infobox_label_port_boblight": "Boblight Server:",
"dashboard_infobox_label_port_flat": "Flatbuffer:",
"dashboard_infobox_label_port_json": "JSON-Server:",
"dashboard_infobox_label_port_proto": "Protobuffer:",

View File

@ -137,7 +137,14 @@ $(document).ready(function () {
} else {
$("#dash_ports_proto_row").hide();
}
if (jQuery.inArray("boblight", window.serverInfo.services) !== -1) {
var boblightPort = window.serverConfig.boblightServer.enable ? window.serverConfig.boblightServer.port : $.i18n('general_disabled');
$('#dash_boblightPort').html(boblightPort);
} else {
$("#dash_ports_boblight_row").hide();
}
var jsonPort = window.serverConfig.jsonServer.port;
$('#dash_jsonPort').html(jsonPort);
var wsPorts = window.serverConfig.webConfig.port + ' | ' + window.serverConfig.webConfig.sslPort;

View File

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

View File

@ -4,113 +4,184 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0);
var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1);
// update instance listing
updateHyperionInstanceListing();
var conf_editor_instCapt = null;
var conf_editor_bobl = null;
// Instance Capture
$('#conf_cont').append(createRow('conf_cont_instCapt'));
$('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
if (window.showOptHelp) {
$('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title")));
if (screenGrabberAvailable || videoGrabberAvailable) {
$('#conf_cont').append(createRow('conf_cont_instCapt'));
$('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
$('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title")));
}
//boblight
if (BOBLIGHT_ENABLED) {
$('#conf_cont').append(createRow('conf_cont_bobl'));
$('#conf_cont_bobl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver', ''));
$('#conf_cont_bobl').append(createHelpTable(window.schema.boblightServer.properties, $.i18n("edt_conf_bobls_heading_title"), "boblightServerHelpPanelId"));
}
}
else {
$('#conf_cont').addClass('row');
if (screenGrabberAvailable || videoGrabberAvailable) {
$('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
}
if (BOBLIGHT_ENABLED) {
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver', ''));
}
}
// Instance Capture
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
instCapture: window.schema.instCapture
}, true, true);
if (screenGrabberAvailable || videoGrabberAvailable) {
var grabber_config_info_html = '<div class="bs-callout bs-callout-info" style="margin-top:0px"><h4>' + $.i18n('dashboard_infobox_label_title') + '</h4 >';
grabber_config_info_html += '<span>' + $.i18n('conf_grabber_inst_grabber_config_info') + '</span>';
grabber_config_info_html += '<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem(\'MenuItemGrabber\')" style="text-decoration:none;cursor:pointer"></a>';
grabber_config_info_html += '</div>';
$('#editor_container_instCapt').append(grabber_config_info_html);
// Instance Capture
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
instCapture: window.schema.instCapture
}, true, true);
conf_editor_instCapt.on('ready', function () {
var grabber_config_info_html = '<div class="bs-callout bs-callout-info" style="margin-top:0px"><h4>' + $.i18n('dashboard_infobox_label_title') + '</h4 >';
grabber_config_info_html += '<span>' + $.i18n('conf_grabber_inst_grabber_config_info') + '</span>';
grabber_config_info_html += '<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem(\'MenuItemGrabber\')" style="text-decoration:none;cursor:pointer"></a>';
grabber_config_info_html += '</div>';
$('#editor_container_instCapt').append(grabber_config_info_html);
if (screenGrabberAvailable) {
if (!window.serverConfig.framegrabber.enable) {
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(false);
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").disable();
}
else {
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(window.serverConfig.instCapture.systemEnable);
}
} else {
showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemEnable", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemGrabberDevice", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemPriority", false);
}
conf_editor_instCapt.on('ready', function () {
if (videoGrabberAvailable) {
if (!window.serverConfig.grabberV4L2.enable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(false);
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable();
}
else {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(window.serverConfig.instCapture.v4lEnable);
}
} else {
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lGrabberDevice", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lEnable", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lPriority", false);
}
});
conf_editor_instCapt.on('change', function () {
if (!conf_editor_instCapt.validate().length) {
if (!window.serverConfig.framegrabber.enable && !window.serverConfig.grabberV4L2.enable) {
$('#btn_submit_instCapt').attr('disabled', true);
if (screenGrabberAvailable) {
if (!window.serverConfig.framegrabber.enable) {
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(false);
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").disable();
}
else {
conf_editor_instCapt.getEditor("root.instCapture.systemEnable").setValue(window.serverConfig.instCapture.systemEnable);
}
} else {
window.readOnlyMode ? $('#btn_submit_instCapt').attr('disabled', true) : $('#btn_submit_instCapt').attr('disabled', false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemEnable", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemGrabberDevice", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "systemPriority", false);
}
}
else {
$('#btn_submit_instCapt').attr('disabled', true);
}
});
conf_editor_instCapt.watch('root.instCapture.systemEnable', () => {
if (videoGrabberAvailable) {
if (!window.serverConfig.grabberV4L2.enable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(false);
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable();
}
else {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").setValue(window.serverConfig.instCapture.v4lEnable);
var screenEnable = conf_editor_instCapt.getEditor("root.instCapture.systemEnable").getValue();
if (screenEnable) {
conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").setValue(window.serverConfig.framegrabber.available_devices);
conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").disable();
showInputOptions("instCapture", ["systemGrabberDevice"], true);
showInputOptions("instCapture", ["systemPriority"], true);
} else {
showInputOptions("instCapture", ["systemGrabberDevice"], false);
showInputOptions("instCapture", ["systemPriority"], false);
}
});
conf_editor_instCapt.watch('root.instCapture.v4lEnable', () => {
console.log("instCapt.watch(root.instCapture.v4lEnable");
var videoEnable = conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").getValue();
if (videoEnable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").setValue(window.serverConfig.grabberV4L2.available_devices);
conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").disable();
showInputOptions("instCapture", ["v4lGrabberDevice"], true);
showInputOptions("instCapture", ["v4lPriority"], true);
}
else {
if (!window.serverConfig.grabberV4L2.enable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable();
}
} else {
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lGrabberDevice", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lEnable", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lPriority", false);
}
showInputOptions("instCapture", ["v4lGrabberDevice"], false);
showInputOptions("instCapture", ["v4lPriority"], false);
}
});
$('#btn_submit_instCapt').off().on('click', function () {
requestWriteConfig(conf_editor_instCapt.getValue());
});
});
conf_editor_instCapt.on('change', function () {
if (!conf_editor_instCapt.validate().length) {
if (!window.serverConfig.framegrabber.enable && !window.serverConfig.grabberV4L2.enable) {
$('#btn_submit_instCapt').attr('disabled', true);
} else {
window.readOnlyMode ? $('#btn_submit_instCapt').attr('disabled', true) : $('#btn_submit_instCapt').attr('disabled', false);
}
}
else {
$('#btn_submit_instCapt').attr('disabled', true);
}
});
conf_editor_instCapt.watch('root.instCapture.systemEnable', () => {
var screenEnable = conf_editor_instCapt.getEditor("root.instCapture.systemEnable").getValue();
if (screenEnable) {
conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").setValue(window.serverConfig.framegrabber.available_devices);
conf_editor_instCapt.getEditor("root.instCapture.systemGrabberDevice").disable();
showInputOptions("instCapture", ["systemGrabberDevice"], true);
showInputOptions("instCapture", ["systemPriority"], true);
} else {
showInputOptions("instCapture", ["systemGrabberDevice"], false);
showInputOptions("instCapture", ["systemPriority"], false);
}
});
conf_editor_instCapt.watch('root.instCapture.v4lEnable', () => {
var videoEnable = conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").getValue();
if (videoEnable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").setValue(window.serverConfig.grabberV4L2.available_devices);
conf_editor_instCapt.getEditor("root.instCapture.v4lGrabberDevice").disable();
showInputOptions("instCapture", ["v4lGrabberDevice"], true);
showInputOptions("instCapture", ["v4lPriority"], true);
}
else {
if (!window.serverConfig.grabberV4L2.enable) {
conf_editor_instCapt.getEditor("root.instCapture.v4lEnable").disable();
}
showInputOptions("instCapture", ["v4lGrabberDevice"], false);
showInputOptions("instCapture", ["v4lPriority"], false);
}
});
$('#btn_submit_instCapt').off().on('click', function () {
requestWriteConfig(conf_editor_instCapt.getValue());
});
}
//boblight
if (BOBLIGHT_ENABLED) {
conf_editor_bobl = createJsonEditor('editor_container_boblightserver', {
boblightServer: window.schema.boblightServer
}, true, true);
conf_editor_bobl.on('ready', function () {
var boblightServerEnable = conf_editor_bobl.getEditor("root.boblightServer.enable").getValue();
if (!boblightServerEnable) {
showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", false);
$('#boblightServerHelpPanelId').hide();
}
});
conf_editor_bobl.on('change', function () {
conf_editor_bobl.validate().length || window.readOnlyMode ? $('#btn_submit_boblightserver').attr('disabled', true) : $('#btn_submit_boblightserver').attr('disabled', false);
});
conf_editor_bobl.watch('root.boblightServer.enable', () => {
var boblightServerEnable = conf_editor_bobl.getEditor("root.boblightServer.enable").getValue();
if (boblightServerEnable) {
//Make port instance specific, if port is still the default one (avoids overlap of used ports)
var port = conf_editor_bobl.getEditor("root.boblightServer.port").getValue();
if (port === conf_editor_bobl.schema.properties.boblightServer.properties.port.default) {
port += parseInt(window.currentHyperionInstance);
}
conf_editor_bobl.getEditor("root.boblightServer.port").setValue(port);
showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", true);
$('#boblightServerHelpPanelId').show();
} else {
showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", false);
$('#boblightServerHelpPanelId').hide();
}
});
$('#btn_submit_boblightserver').off().on('click', function () {
requestWriteConfig(conf_editor_bobl.getValue());
});
}
//create introduction
if (window.showOptHelp) {
if (BOBLIGHT_ENABLED) {
createHint("intro", $.i18n('conf_network_bobl_intro'), "editor_container_boblightserver");
}
}
removeOverlay();
});

View File

@ -277,7 +277,7 @@ function createClassicLeds() {
aceEdt.set(finalLedArray);
}
function createMatrixLayout(ledshoriz, ledsvert, cabling, start) {
function createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction) {
// Big thank you to RanzQ (Juha Rantanen) from Github for this script
// https://raw.githubusercontent.com/RanzQ/hyperion-audio-effects/master/matrix-config.js
@ -325,15 +325,30 @@ function createMatrixLayout(ledshoriz, ledsvert, cabling, start) {
var x, y
for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) {
if (direction === 'vertical') {
for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) {
addLed(x, y)
for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) {
addLed(x, y)
}
if (!parallel) {
downward = !downward
var tmp = startY
startY = endY
endY = tmp
}
}
if (!parallel) {
forward = !forward
var tmp = startX
startX = endX
endX = tmp
} else {
for (y = startY; downward && y <= endY || !downward && y >= endY; y += downward ? 1 : -1) {
for (x = startX; forward && x <= endX || !forward && x >= endX; x += forward ? 1 : -1) {
addLed(x, y)
}
if (!parallel) {
forward = !forward
var tmp = startX
startX = endX
endX = tmp
}
}
}
@ -348,9 +363,10 @@ function createMatrixLeds() {
var ledshoriz = parseInt($("#ip_ma_ledshoriz").val());
var ledsvert = parseInt($("#ip_ma_ledsvert").val());
var cabling = $("#ip_ma_cabling").val();
var direction = $("#ip_ma_direction").val();
var start = $("#ip_ma_start").val();
nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start);
nonBlacklistLedArray = createMatrixLayout(ledshoriz, ledsvert, cabling, start, direction);
finalLedArray = blackListLeds(nonBlacklistLedArray, ledBlacklist);
createLedPreview(finalLedArray, 'matrix');
@ -467,7 +483,63 @@ $(document).ready(function () {
// bind change event to all inputs
$('.ledCLconstr').bind("change", function () {
valValue(this.id, this.value, this.min, this.max);
//Ensure Values are in min/max ranges
if ($(this).val() < $(this).attr('min') * 1) { $(this).val($(this).attr('min')); }
if ($(this).val() > $(this).attr('max') * 1) { $(this).val($(this).attr('max')); }
//top/bottom and left/right must not overlap
switch (this.id) {
case "ip_cl_ptlh":
var ptrh = parseInt($("#ip_cl_ptrh").val());
if (this.value > ptrh) {
$(this).val(ptrh);
}
break;
case "ip_cl_ptrh":
var ptlh = parseInt($("#ip_cl_ptlh").val());
if (this.value < ptlh) {
$(this).val(ptlh);
}
break;
case "ip_cl_pblh":
var pbrh = parseInt($("#ip_cl_pbrh").val());
if (this.value > pbrh) {
$(this).val(pbrh);
}
break;
case "ip_cl_pbrh":
var pblh = parseInt($("#ip_cl_pblh").val());
if (this.value < pblh) {
$(this).val(pblh);
}
break;
case "ip_cl_ptlv":
var pblv = parseInt($("#ip_cl_pblv").val());
if (this.value > pblv) {
$(this).val(pblv);
}
break;
case "ip_cl_pblv":
var ptlv = parseInt($("#ip_cl_ptlv").val());
if (this.value < ptlv) {
$(this).val(ptlv);
}
break;
case "ip_cl_ptrv":
var pbrv = parseInt($("#ip_cl_pbrv").val());
if (this.value > pbrv) {
$(this).val(pbrv);
}
break;
case "ip_cl_pbrv":
var ptrv = parseInt($("#ip_cl_ptrv").val());
if (this.value < ptrv) {
$(this).val(ptrv);
}
default:
}
createClassicLeds();
});
@ -1591,6 +1663,7 @@ async function getProperties_device(ledType, key, params) {
}
}
else {
showNotification('warning', $.i18n('conf_leds_error_get_properties_text'), $.i18n('conf_leds_error_get_properties_title'))
$('#btn_submit_controller').attr('disabled', true);
$('#btn_test_controller').attr('disabled', true);
}
@ -1691,6 +1764,7 @@ function updateElements(ledType, key) {
$("#ip_ma_ledshoriz").val(ledProperties.maxColumn);
$("#ip_ma_ledsvert").val(ledProperties.maxRow);
$("#ip_ma_cabling").val("parallel");
$("#ip_ma_direction").val("horizontal");
$("#ip_ma_start").val("top-left");
createMatrixLeds();
}

View File

@ -1,7 +1,6 @@
$(document).ready(function () {
performTranslation();
var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1);
var FORWARDER_ENABLED = (jQuery.inArray("forwarder", window.serverInfo.services) !== -1);
var FLATBUF_SERVER_ENABLED = (jQuery.inArray("flatbuffer", window.serverInfo.services) !== -1);
var PROTOTBUF_SERVER_ENABLED = (jQuery.inArray("protobuffer", window.serverInfo.services) !== -1);
@ -10,7 +9,6 @@ $(document).ready(function () {
var conf_editor_json = null;
var conf_editor_proto = null;
var conf_editor_fbs = null;
var conf_editor_bobl = null;
var conf_editor_forw = null;
addJsonEditorHostValidation();
@ -40,13 +38,6 @@ $(document).ready(function () {
$('#conf_cont_proto').append(createHelpTable(window.schema.protoServer.properties, $.i18n("edt_conf_pbs_heading_title"), "protoServerHelpPanelId"));
}
//boblight
if (BOBLIGHT_ENABLED) {
$('#conf_cont').append(createRow('conf_cont_bobl'));
$('#conf_cont_bobl').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver', 'panel-system'));
$('#conf_cont_bobl').append(createHelpTable(window.schema.boblightServer.properties, $.i18n("edt_conf_bobls_heading_title"), "boblightServerHelpPanelId"));
}
//forwarder
if (FORWARDER_ENABLED) {
if (storedAccess != 'default') {
@ -66,9 +57,6 @@ $(document).ready(function () {
if (PROTOTBUF_SERVER_ENABLED) {
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_pbs_heading_title"), 'editor_container_protoserver', 'btn_submit_protoserver'));
}
if (BOBLIGHT_ENABLED) {
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_bobls_heading_title"), 'editor_container_boblightserver', 'btn_submit_boblightserver'));
}
if (FORWARDER_ENABLED) {
$('#conf_cont').append(createOptPanel('fa-sitemap', $.i18n("edt_conf_fw_heading_title"), 'editor_container_forwarder', 'btn_submit_forwarder'));
}
@ -147,29 +135,6 @@ $(document).ready(function () {
});
}
//boblight
if (BOBLIGHT_ENABLED) {
conf_editor_bobl = createJsonEditor('editor_container_boblightserver', {
boblightServer: window.schema.boblightServer
}, true, true);
conf_editor_bobl.on('change', function () {
var boblightServerEnable = conf_editor_bobl.getEditor("root.boblightServer.enable").getValue();
if (boblightServerEnable) {
showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", true);
$('#boblightServerHelpPanelId').show();
} else {
showInputOptionsForKey(conf_editor_bobl, "boblightServer", "enable", false);
$('#boblightServerHelpPanelId').hide();
}
conf_editor_bobl.validate().length || window.readOnlyMode ? $('#btn_submit_boblightserver').attr('disabled', true) : $('#btn_submit_boblightserver').attr('disabled', false);
});
$('#btn_submit_boblightserver').off().on('click', function () {
requestWriteConfig(conf_editor_bobl.getValue());
});
}
//forwarder
if (FORWARDER_ENABLED) {
if (storedAccess != 'default') {
@ -205,9 +170,6 @@ $(document).ready(function () {
if (PROTOTBUF_SERVER_ENABLED) {
createHint("intro", $.i18n('conf_network_proto_intro'), "editor_container_protoserver");
}
if (BOBLIGHT_ENABLED) {
createHint("intro", $.i18n('conf_network_bobl_intro'), "editor_container_boblightserver");
}
if (FORWARDER_ENABLED) {
createHint("intro", $.i18n('conf_network_forw_intro'), "editor_container_forwarder");
}

View File

@ -128,13 +128,13 @@ $(document).ready(function () {
switch (compId) {
case "EFFECT":
owner = $.i18n('remote_effects_label_effects') + ' ' + owner;
owner = $.i18n('remote_effects_label_effects') + ' ' + owner;
break;
case "COLOR":
owner = $.i18n('remote_color_label_color') + ' ' + '<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;
case "IMAGE":
owner = $.i18n('remote_effects_label_picture') + ' ' + owner;
owner = $.i18n('remote_effects_label_picture') + (owner !== undefined ? (' ' + owner): "");
break;
case "GRABBER":
owner = $.i18n('general_comp_GRABBER') + ': (' + owner + ')';
@ -153,16 +153,19 @@ $(document).ready(function () {
break;
}
if (duration && compId != "GRABBER" && compId != "FLATBUFSERVER" && compId != "PROTOSERVER")
owner += '<br/><span style="font-size:80%; color:grey;">' + $.i18n('remote_input_duration') + ' ' + duration.toFixed(0) + $.i18n('edt_append_s') + '</span>';
if (!(duration && duration < 0))
{
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>';
var btn = '<button id="srcBtn' + i + '" type="button" ' + btn_state + ' class="btn btn-' + btn_type + ' btn_input_selection" onclick="requestSetSource(' + priority + ');">' + btn_text + '</button>';
var btn = '<button id="srcBtn' + i + '" type="button" ' + btn_state + ' class="btn btn-' + btn_type + ' btn_input_selection" onclick="requestSetSource(' + priority + ');">' + btn_text + '</button>';
if ((compId == "EFFECT" || compId == "COLOR" || compId == "IMAGE") && priority < 254)
btn += '<button type="button" class="btn btn-sm btn-danger" style="margin-left:10px;" onclick="requestPriorityClear(' + priority + ');"><i class="fa fa-close"></button>';
if ((compId == "EFFECT" || compId == "COLOR" || compId == "IMAGE") && priority < 254)
btn += '<button type="button" class="btn btn-sm btn-danger" style="margin-left:10px;" onclick="requestPriorityClear(' + priority + ');"><i class="fa fa-close"></button>';
if (btn_type != 'default')
$('.sstbody').append(createTableRow([origin, owner, priority, btn], false, true));
if (btn_type != 'default')
$('.sstbody').append(createTableRow([origin, owner, priority, btn], false, true));
}
}
var btn_auto_color = (window.serverInfo.priorities_autoselect ? "btn-success" : "btn-danger");
var btn_auto_state = (window.serverInfo.priorities_autoselect ? "disabled" : "enabled");
@ -313,6 +316,9 @@ $(document).ready(function () {
if (getStorage('rmduration') != null) {
$("#remote_duration").val(getStorage('rmduration'));
duration = getStorage('rmduration');
if (duration == 0) {
duration = ENDLESS;
}
}
createCP('cp2', cpcolor, function (rgbT, hex) {
@ -332,6 +338,9 @@ $(document).ready(function () {
$("#remote_duration").off().on("change", function () {
duration = valValue(this.id, this.value, this.min, this.max);
setStorage('rmduration', duration);
if (duration == 0) {
duration = ENDLESS;
}
});
$("#effect_select").off().on("change", function (event) {
@ -349,8 +358,9 @@ $(document).ready(function () {
});
$("#remote_input_repimg").off().on("click", function () {
if (lastImgData != "")
if (lastImgData != "") {
requestSetImage(lastImgData, duration, lastFileName);
}
});
$("#remote_input_img").change(function () {
@ -380,7 +390,6 @@ $(document).ready(function () {
// interval updates
$(window.hyperion).on('components-updated', function (e, comp) {
//console.log ("components-updated", e, comp);
updateComponent(comp);
});

View File

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

View File

@ -1,111 +1,117 @@
$(document).ready( function() {
$(document).ready(function () {
// check if browser supports streaming
if(window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia){
$("#btn_streamer").toggle();
}
// check if browser supports streaming
if (window.navigator.mediaDevices && window.navigator.mediaDevices.getDisplayMedia) {
$("#btn_streamer").toggle();
}
// variables
var streamActive = false;
var screenshotTimer = "";
var screenshotIntervalTimeMs = 100;
var streamImageHeight = 0;
var streamImageWidth = 0;
const videoElem = document.getElementById("streamvideo");
const canvasElem = document.getElementById("streamcanvas");
// variables
var streamActive = false;
var screenshotTimer = "";
var screenshotIntervalTimeMs = 100;
var streamImageHeight = 0;
var streamImageWidth = 0;
const videoElem = document.getElementById("streamvideo");
const canvasElem = document.getElementById("streamcanvas");
// Options for getDisplayMedia()
var displayMediaOptions = {
video: {
cursor: "never",
width: 170,
height: 100,
frameRate: 15
},
audio: false
};
// Options for getDisplayMedia()
var displayMediaOptions = {
video: {
cursor: "never",
width: 170,
height: 100,
frameRate: 15
},
audio: false
};
async function startCapture() {
streamActive = true;
async function startCapture() {
streamActive = true;
try {
var stream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
videoElem.srcObject = stream;
try {
var stream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
videoElem.srcObject = stream;
// get the active track of the stream
const track = stream.getVideoTracks()[0];
// get the active track of the stream
const track = stream.getVideoTracks()[0];
// listen for track ending, fires when user aborts through browser
track.onended = function (event) {
stopCapture();
};
// listen for track ending, fires when user aborts through browser
track.onended = function(event) {
stopCapture();
};
// wait for video ready
videoElem.addEventListener('loadedmetadata', (e) => {
window.setTimeout(() => (
onCapabilitiesReady(track.getSettings())
), 500);
});
} catch (err) {
stopCapture();
console.error("Error: " + err);
}
}
// wait for video ready
videoElem.addEventListener('loadedmetadata', (e) => {
window.setTimeout(() => (
onCapabilitiesReady(track.getSettings())
), 500);
});
} catch(err) {
stopCapture();
console.error("Error: " + err);
}
}
function onCapabilitiesReady(settings) {
// extract real width/height
streamImageWidth = settings.width;
streamImageHeight = settings.height;
function onCapabilitiesReady(settings) {
// extract real width/height
streamImageWidth = settings.width;
streamImageHeight = settings.height;
// start screenshotTimer
updateScrTimer(false);
// start screenshotTimer
updateScrTimer(false);
// we are sending
$("#btn_streamer_icon").addClass("text-danger");
}
// we are sending
$("#btn_streamer_icon").addClass("text-danger");
}
function stopCapture(evt) {
streamActive = false;
$("#btn_streamer_icon").removeClass("text-danger");
function stopCapture(evt) {
streamActive = false;
$("#btn_streamer_icon").removeClass("text-danger");
updateScrTimer(true);
// sometimes it's null on abort
if (videoElem.srcObject) {
let tracks = videoElem.srcObject.getTracks();
updateScrTimer(true);
// sometimes it's null on abort
if(videoElem.srcObject){
let tracks = videoElem.srcObject.getTracks();
tracks.forEach(track => track.stop());
videoElem.srcObject = null;
}
requestPriorityClear(1);
}
tracks.forEach(track => track.stop());
videoElem.srcObject = null;
}
}
function takePicture() {
var context = canvasElem.getContext('2d');
canvasElem.width = streamImageWidth;
canvasElem.height = streamImageHeight;
context.drawImage(videoElem, 0, 0, streamImageWidth, streamImageHeight);
function takePicture(){
var context = canvasElem.getContext('2d');
canvasElem.width = streamImageWidth;
canvasElem.height = streamImageHeight;
context.drawImage(videoElem, 0, 0, streamImageWidth, streamImageHeight);
var data = canvasElem.toDataURL('image/png').split(",")[1];
requestSetImage(data, -1, "Streaming");
}
var data = canvasElem.toDataURL('image/png').split(",")[1];
requestSetImage(data, 2, "Streaming");
}
// start or update screenshot timer
function updateScrTimer(stop) {
clearInterval(screenshotTimer)
// start or update screenshot timer
function updateScrTimer(stop){
clearInterval(screenshotTimer)
if (stop === false) {
screenshotTimer = setInterval(() => (
takePicture()
), screenshotIntervalTimeMs);
}
}
if(stop === false){
screenshotTimer = setInterval(() => (
takePicture()
), screenshotIntervalTimeMs);
}
}
$("#btn_streamer").off().on("click", function (e) {
if (!$("#btn_streamer_icon").hasClass("text-danger") && !streamActive) {
startCapture();
} else {
stopCapture();
}
});
$("#btn_streamer").off().on("click",function(e){
if(!$("#btn_streamer_icon").hasClass("text-danger") && !streamActive){
startCapture();
} else {
stopCapture();
}
});
$(window.hyperion).on("stopBrowerScreenCapture", function (event) {
if (streamActive) {
stopCapture();
}
});
});

View File

@ -1221,13 +1221,13 @@ function getSystemInfo() {
info += '- Architecture: ' + sys.architecture + '\n';
if (sys.cpuModelName)
info += '- CPU Model: ' + sys.cpuModelName + '\n';
info += '- CPU Model: ' + sys.cpuModelName + '\n';
if (sys.cpuModelType)
info += '- CPU Type: ' + sys.cpuModelType + '\n';
info += '- CPU Type: ' + sys.cpuModelType + '\n';
if (sys.cpuRevision)
info += '- CPU Revision: ' + sys.cpuRevision + '\n';
info += '- CPU Revision: ' + sys.cpuRevision + '\n';
if (sys.cpuHardware)
info += '- CPU Hardware: ' + sys.cpuHardware + '\n';
info += '- CPU Hardware: ' + sys.cpuHardware + '\n';
info += '- Kernel: ' + sys.kernelType + ' (' + sys.kernelVersion + ' (WS: ' + sys.wordSize + '))\n';
info += '- Root/Admin: ' + sys.isUserAdmin + '\n';

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>';
}
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_btn_sp').off().on('click', function () {
switchPicture(["VGradient", "grey_1", "grey_2", "grey_3", "HGradient"]);
@ -1534,8 +1534,8 @@ async function discover_yeelight_lights() {
function assign_yeelight_lights() {
// Model mappings, see https://www.home-assistant.io/integrations/yeelight/
var models = ['color', 'color1', 'YLDP02YL', 'YLDP02YL', 'color2', 'YLDP06YL', 'color4', 'YLDP13YL', 'stripe', 'YLDD04YL', 'strip1', 'YLDD01YL', 'YLDD02YL'];
var models = ['color', 'color1', 'YLDP02YL', 'YLDP02YL', 'color2', 'YLDP06YL', 'color4', 'YLDP13YL', 'color6', 'YLDP13AYL', 'colorc', "YLDP004-A", 'stripe', 'YLDD04YL', 'strip1', 'YLDD01YL', 'YLDD02YL', 'strip4', 'YLDD05YL', 'strip6', 'YLDD05YL'];
// If records are left for configuration
if (Object.keys(lights).length > 0) {
$('#wh_topcontainer').toggle(false);

View File

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

View File

@ -336,9 +336,9 @@ protected:
bool getUserToken(QString &userToken);
///
/// @brief Is a token authrized. On success this will grant acces to the API (NOT ADMIN API)
/// @brief Is a token authorized. On success this will grant acces to the API (NOT ADMIN API)
/// @param token The user Token
/// @return True on succes
/// @return True on success
///
bool isTokenAuthorized(const QString &token);

View File

@ -17,6 +17,8 @@
// AuthManager
#include <hyperion/AuthManager.h>
#include <hyperion/PriorityMuxer.h>
class Hyperion;
class ComponentRegister;
class BonjourBrowserWrapper;
@ -78,10 +80,13 @@ private slots:
///
void handleBonjourChange(const QMap<QString,BonjourRecord>& bRegisters);
#endif
///
/// @brief handle emits from PriorityMuxer
/// @param currentPriority The current priority at time of emit
/// @param activeInputs The current active input map at time of emit
///
void handlePriorityUpdate();
void handlePriorityUpdate(int currentPriority, const PriorityMuxer::InputsMap& activeInputs);
///
/// @brief Handle imageToLedsMapping updates

View File

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

View File

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

View File

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

View File

@ -54,6 +54,8 @@ public:
QString owner;
};
typedef QMap<int, InputInfo> InputsMap;
//Foreground and Background priorities
const static int FG_PRIORITY;
const static int BG_PRIORITY;
@ -62,6 +64,7 @@ public:
const static int LOWEST_PRIORITY;
/// Timeout used to identify a non active priority
const static int TIMEOUT_NOT_ACTIVE_PRIO;
const static int REMOVE_CLEARED_PRIO;
const static int ENDLESS;
@ -197,22 +200,13 @@ public:
///
void clearAll(bool forceClearAll=false);
///
/// @brief Queue a manual push where muxer doesn't recognize them (e.g. continuous single color pushes)
///
void queuePush() { emit timeRunner(); }
signals:
///
/// @brief Signal which emits when a effect or color with timeout > -1 is running, once per second
///
void timeRunner();
///
/// @brief Emits whenever the visible priority has changed
/// @param priority The new visible priority
///
void visiblePriorityChanged(quint8 priority);
void visiblePriorityChanged(int priority);
///
/// @brief Emits whenever the current visible component changed
@ -222,9 +216,13 @@ signals:
///
/// @brief Emits whenever something changes which influences the priorities listing
/// Emits also in 1s interval when a COLOR or EFFECT is running with a timeout > -1 (endless)
/// Emits also in 1s interval when a COLOR or EFFECT is running with a timeout > -1
/// @param currentPriority The current priority at time of emit
/// @param activeInputs The current active input map at time of emit
///
void prioritiesChanged();
void prioritiesChanged(int currentPriority, InputsMap activeInputs);
///
/// internal used signal to resolve treading issues with timer
@ -233,15 +231,15 @@ signals:
private slots:
///
/// Slot which is called to adapt to 1s interval for signal timeRunner() / prioritiesChanged()
/// Slot which is called to adapt to 1s interval for signal prioritiesChanged()
///
void timeTrigger();
///
/// Updates the current time. Channels with a configured time out will be checked and cleared if
/// required.
/// Updates the current priorities. Channels with a configured time out will be checked and cleared if
/// required. Cleared priorities will be removed.
///
void setCurrentTime();
void updatePriorities();
private:
///
@ -266,7 +264,7 @@ private:
hyperion::Components _prevVisComp = hyperion::COMP_INVALID;
/// The mapping from priority channel to led-information
QMap<int, InputInfo> _activeInputs;
InputsMap _activeInputs;
/// The information of the lowest priority channel
InputInfo _lowestPriorityInfo;

View File

@ -29,7 +29,7 @@ namespace NetUtils {
server.close();
if(port != prevPort)
{
Warning(log, "The requested Port '%d' was already in use, will use Port '%d' instead", prevPort, port);
Warning(log, "The requested Port '%d' is already in use, will use Port '%d' instead", prevPort, port);
return false;
}
return true;

View File

@ -10,6 +10,7 @@
#include <QDateTime>
#include <QCryptographicHash>
#include <QImage>
#include <QImageReader>
#include <QBuffer>
#include <QByteArray>
#include <QTimer>
@ -83,15 +84,19 @@ void API::init()
}
// if this is localConnection and network allows unauth locals, set authorized flag
if (apiAuthRequired && _localConnection)
{
_authorized = !_authManager->isLocalAuthRequired();
}
// admin access is allowed, when the connection is local and the option for local admin isn't set. Con: All local connections get full access
if (_localConnection)
{
_adminAuthorized = !_authManager->isLocalAdminAuthRequired();
// just in positive direction
if (_adminAuthorized)
_authorized = true;
if (_adminAuthorized)
{
_authorized = true;
}
}
}
@ -113,12 +118,25 @@ bool API::setImage(ImageCmdData &data, hyperion::Components comp, QString &reply
// truncate name length
data.imgName.truncate(16);
if (data.format == "auto")
{
QImage img = QImage::fromData(data.data);
if (!data.format.isEmpty())
{
if (data.format == "auto")
{
data.format = "";
}
else
{
if (!QImageReader::supportedImageFormats().contains(data.format.toLower().toUtf8()))
{
replyMsg = "The given format [" + data.format + "] is not supported";
return false;
}
}
QImage img = QImage::fromData(data.data, QSTRING_CSTR(data.format));
if (img.isNull())
{
replyMsg = "Failed to parse picture, the file might be corrupted";
replyMsg = "Failed to parse picture, the file might be corrupted or content does not match the given format [" + data.format + "]";
return false;
}

View File

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

View File

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

View File

@ -55,11 +55,7 @@ BoblightClientConnection::BoblightClientConnection(Hyperion* hyperion, QTcpSocke
BoblightClientConnection::~BoblightClientConnection()
{
// clear the current channel
if (_priority != 0 && _priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY)
_hyperion->clear(_priority);
delete _socket;
_socket->deleteLater();
}
void BoblightClientConnection::readData()
@ -117,9 +113,10 @@ QString BoblightClientConnection::readMessage(const char* data, const size_t siz
void BoblightClientConnection::socketClosed()
{
// clear the current channel
if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY)
{
_hyperion->clear(_priority);
}
emit connectionClosed(this);
}
@ -205,40 +202,58 @@ void BoblightClientConnection::handleMessage(const QString& message)
{
bool rc;
const int prio = static_cast<int>(parseUInt(messageParts[2], &rc));
if (rc && prio != _priority)
if (rc)
{
if (_priority != 0 && _hyperion->getPriorityInfo(_priority).componentId == hyperion::COMP_BOBLIGHTSERVER)
_hyperion->clear(_priority);
int currentPriority = _hyperion->getCurrentPriority();
if (prio < BOBLIGHT_MIN_PRIORITY || prio > BOBLIGHT_MAX_PRIORITY)
if (prio == currentPriority)
{
_priority = BOBLIGHT_DEFAULT_PRIORITY;
while (_hyperion->getActivePriorities().contains(_priority))
{
_priority += 1;
}
// warn against invalid priority
Warning(_log, "The priority %i is not in the priority range of [%d-%d]. Priority %i is used instead.",
prio, BOBLIGHT_MIN_PRIORITY, BOBLIGHT_MAX_PRIORITY, _priority);
// register new priority (previously modified)
_hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_socket->peerAddress().toString()));
Error(_log, "The priority %i is already in use onther component of type [%s]", prio, componentToString(_hyperion->getPriorityInfo(currentPriority).componentId));
_socket->close();
}
else
{
// register new priority
_hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_socket->peerAddress().toString()));
_priority = prio;
}
if (prio < BOBLIGHT_MIN_PRIORITY || prio > BOBLIGHT_MAX_PRIORITY)
{
_priority = BOBLIGHT_DEFAULT_PRIORITY;
while (_hyperion->getActivePriorities().contains(_priority))
{
_priority += 1;
}
return;
// warn against invalid priority
Warning(_log, "The priority %i is not in the priority range of [%d-%d]. Priority %i is used instead.",
prio, BOBLIGHT_MIN_PRIORITY, BOBLIGHT_MAX_PRIORITY, _priority);
// register new priority (previously modified)
_hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress));
}
else
{
// register new priority
_hyperion->registerInput(prio, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress));
_priority = prio;
}
}
}
return;
}
}
else if (messageParts[0] == "sync")
{
if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY)
_hyperion->setInput(_priority, _ledColors); // send current color values to hyperion
{
int currentPriority = _hyperion->getCurrentPriority();
if ( _priority != currentPriority)
{
// register this connection's priority
_hyperion->registerInput(_priority, hyperion::COMP_BOBLIGHTSERVER, QString("Boblight@%1").arg(_clientAddress));
}
if (_priority >= BOBLIGHT_MIN_PRIORITY && _priority <= BOBLIGHT_MAX_PRIORITY)
{
_hyperion->setInput(_priority, _ledColors); // send current color values to hyperion
}
}
return;
}
@ -387,6 +402,14 @@ uint8_t BoblightClientConnection::parseByte(const QString& s, bool *ok) const
return static_cast<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()
{
char buffer[256];

View File

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

View File

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

View File

@ -219,7 +219,7 @@ void EffectEngine::effectFinished()
_hyperion->clear(effect->getPriority());
}
Info( _log, "effect finished");
Info( _log, "Effect [%s] finished", QSTRING_CSTR(effect->getName()));
for (auto effectIt = _activeEffects.begin(); effectIt != _activeEffects.end(); ++effectIt)
{
if (*effectIt == effect)

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();
if (priority != 0 && _forwarder_enabled && obj["enable"].toBool())

View File

@ -66,9 +66,9 @@ bool QtGrabber::open()
bool QtGrabber::setupDisplay()
{
bool result = false;
if ( ! open() )
if (!open())
{
if ( _isWayland )
if (_isWayland)
{
Error(_log, "Grabber does not work under Wayland!");
}
@ -80,45 +80,46 @@ bool QtGrabber::setupDisplay()
_numberOfSDisplays = 0;
QScreen* primary = QGuiApplication::primaryScreen();
QList<QScreen *> screens = QGuiApplication::screens();
QList<QScreen*> screens = QGuiApplication::screens();
// inject main screen at 0, if not nullptr
if(primary != nullptr)
if (primary != nullptr)
{
screens.prepend(primary);
// remove last main screen if twice in list
if(screens.lastIndexOf(primary) > 0)
if (screens.lastIndexOf(primary) > 0)
{
screens.removeAt(screens.lastIndexOf(primary));
}
}
if(screens.isEmpty())
if (screens.isEmpty())
{
Error(_log, "No displays found to capture from!");
result = false;
result = false;
}
else
{
_numberOfSDisplays = screens.size();
Info(_log,"Available Displays:");
Info(_log, "Available Displays:");
int index = 0;
for(auto * screen : qAsConst(screens))
for (auto* screen : qAsConst(screens))
{
const QRect geo = screen->geometry();
Info(_log,"Display %d: Name: %s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.left(), geo.top() ,geo.right(), geo.bottom(), screen->depth());
Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", index, QSTRING_CSTR(screen->name()), geo.width(), geo.height(), geo.x(), geo.y(), geo.x() + geo.width(), geo.y() + geo.height(), screen->depth());
++index;
}
if (screens.at(0)->size() != screens.at(0)->virtualSize())
{
const QRect vgeo = screens.at(0)->virtualGeometry();
Info(_log,"Display %d: Name: %s Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", _numberOfSDisplays, "All Displays", vgeo.left(), vgeo.top() ,vgeo.right(), vgeo.bottom(), screens.at(0)->depth());
Info(_log, "Display %d: Name: %s Resolution: [%dx%d], Geometry: (L,T,R,B) %d,%d,%d,%d Depth:%dbit", _numberOfSDisplays, "All Displays", vgeo.width(), vgeo.height(), vgeo.x(), vgeo.y(), vgeo.x() + vgeo.width(), vgeo.y() + vgeo.height(), screens.at(0)->depth());
}
_isVirtual = false;
// be sure the index is available
if (_display > _numberOfSDisplays - 1 )
if (_display > _numberOfSDisplays - 1)
{
if ((screens.at(0)->size() != screens.at(0)->virtualSize()) && (_display == _numberOfSDisplays))
@ -145,17 +146,17 @@ bool QtGrabber::setupDisplay()
}
else
{
Info(_log,"Initialized display %d", _display);
Info(_log, "Initialized display %d", _display);
}
result = true;
result = true;
}
}
return result;
}
void QtGrabber::geometryChanged(const QRect &geo)
void QtGrabber::geometryChanged(const QRect& geo)
{
Info(_log, "The current display changed geometry to (L,T,R,B) %d,%d,%d,%d", geo.left(), geo.top() ,geo.right(), geo.bottom());
Info(_log, "The current display changed geometry to (L,T,R,B) %d,%d,%d,%d", geo.left(), geo.top(), geo.x() + geo.width(), geo.y() + geo.height());
updateScreenDimensions(true);
}
@ -165,8 +166,6 @@ extern QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int format = 0);
QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int height) const
{
QSize windowSize;
int x = xIn;
int y = yIn;
HWND hwnd = reinterpret_cast<HWND>(window);
if (hwnd)
{
@ -179,15 +178,13 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int
hwnd = GetDesktopWindow();
const QRect screenGeometry = _screen->geometry();
windowSize = screenGeometry.size();
x += screenGeometry.x();
y += screenGeometry.y();
}
if (width < 0)
width = windowSize.width() - x;
width = windowSize.width() - xIn;
if (height < 0)
height = windowSize.height() - y;
height = windowSize.height() - yIn;
// Create and setup bitmap
HDC display_dc = GetDC(nullptr);
@ -197,7 +194,7 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int
// copy data
HDC window_dc = GetDC(hwnd);
BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY);
BitBlt(bitmap_dc, 0, 0, width, height, window_dc, xIn, yIn, SRCCOPY);
// clean up all but bitmap
ReleaseDC(hwnd, window_dc);
@ -211,12 +208,12 @@ QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int
}
#endif
int QtGrabber::grabFrame(Image<ColorRgb> & image)
int QtGrabber::grabFrame(Image<ColorRgb>& image)
{
int rc = 0;
if (_isEnabled && !_isDeviceInError)
{
if(_screen == nullptr)
if (_screen == nullptr)
{
// reinit, this will disable capture on failure
bool result = setupDisplay();
@ -229,14 +226,14 @@ int QtGrabber::grabFrame(Image<ColorRgb> & image)
QPixmap originalPixmap = grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
#else
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
#endif
#endif
if (originalPixmap.isNull())
{
rc = -1;
}
else
{
QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat( QImage::Format_RGB888);
QImage imageFrame = originalPixmap.toImage().scaled(_calculatedWidth, _calculatedHeight).convertToFormat(QImage::Format_RGB888);
image.resize(static_cast<uint>(_calculatedWidth), static_cast<uint>(_calculatedHeight));
for (int y = 0; y < imageFrame.height(); y++)
@ -251,7 +248,7 @@ int QtGrabber::grabFrame(Image<ColorRgb> & image)
int QtGrabber::updateScreenDimensions(bool force)
{
if(_screen == nullptr)
if (_screen == nullptr)
{
return -1;
}
@ -273,53 +270,65 @@ int QtGrabber::updateScreenDimensions(bool force)
}
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _width, _height, geo.width(), geo.height());
_width = geo.width();
_width = geo.width();
_height = geo.height();
int width=0;
int height=0;
int width = 0;
int height = 0;
// Image scaling is performed by Qt
width = (_width > (_cropLeft + _cropRight))
width = (_width > (_cropLeft + _cropRight))
? ((_width - _cropLeft - _cropRight) / _pixelDecimation)
: (_width / _pixelDecimation);
height = (_height > (_cropTop + _cropBottom))
height = (_height > (_cropTop + _cropBottom))
? ((_height - _cropTop - _cropBottom) / _pixelDecimation)
: (_height / _pixelDecimation);
// calculate final image dimensions and adjust top/left cropping in 3D modes
if (_isVirtual)
{
_src_x = geo.x();
_src_y = geo.y();
}
else
{
_src_x = 0;
_src_y = 0;
}
switch (_videoMode)
{
case VideoMode::VIDEO_3DSBS:
_calculatedWidth = width /2;
_calculatedWidth = width / 2;
_calculatedHeight = height;
_src_x = _cropLeft / 2;
_src_y = _cropTop;
_src_x = _src_x + (_cropLeft / 2);
_src_y = _src_y + _cropTop;
_src_x_max = (_width / 2) - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop;
break;
case VideoMode::VIDEO_3DTAB:
_calculatedWidth = width;
_calculatedWidth = width;
_calculatedHeight = height / 2;
_src_x = _cropLeft;
_src_y = _cropTop / 2;
_src_x = _src_x + _cropLeft;
_src_y = _src_y + (_cropTop / 2);
_src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = (_height / 2) - _cropBottom - _cropTop;
break;
case VideoMode::VIDEO_2D:
default:
_calculatedWidth = width;
_calculatedWidth = width;
_calculatedHeight = height;
_src_x = _cropLeft;
_src_y = _cropTop;
_src_x = _src_x + _cropLeft;
_src_y = _src_y + _cropTop;
_src_x_max = _width - _cropRight - _cropLeft;
_src_y_max = _height - _cropBottom - _cropTop;
break;
}
Info(_log, "Update output image resolution to [%dx%d]", _calculatedWidth, _calculatedHeight);
Debug(_log, "Grab screen area: %d,%d,%d,%d", _src_x, _src_y, _src_x_max, _src_y_max);
return 1;
}
@ -331,10 +340,10 @@ void QtGrabber::setVideoMode(VideoMode mode)
bool QtGrabber::setPixelDecimation(int pixelDecimation)
{
bool rc (true);
if(Grabber::setPixelDecimation(pixelDecimation))
bool rc(true);
if (Grabber::setPixelDecimation(pixelDecimation))
{
if ( updateScreenDimensions(true) < 0)
if (updateScreenDimensions(true) < 0)
{
rc = false;
}
@ -350,14 +359,20 @@ void QtGrabber::setCropping(int cropLeft, int cropRight, int cropTop, int cropBo
bool QtGrabber::setDisplayIndex(int index)
{
bool rc (true);
if (_display != index)
bool rc(true);
if (_display != index || _isVirtual)
{
_isVirtual = false;
if (index <= _numberOfSDisplays)
{
_display = index;
if (index == _numberOfSDisplays)
{
_isVirtual = true;
}
}
else {
else
{
_display = 0;
}
rc = setupDisplay();
@ -370,7 +385,7 @@ QJsonObject QtGrabber::discover(const QJsonObject& params)
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
QJsonObject inputsDiscovered;
if ( open() )
if (open())
{
QList<QScreen*> screens = QGuiApplication::screens();
if (!screens.isEmpty())
@ -390,7 +405,7 @@ QJsonObject QtGrabber::discover(const QJsonObject& params)
int pos = name.lastIndexOf('\\');
if (pos != -1)
{
name = name.right(name.length()-pos-1);
name = name.right(name.length() - pos - 1);
}
in["name"] = name;
@ -460,5 +475,4 @@ QJsonObject QtGrabber::discover(const QJsonObject& params)
DebugIf(verbose, _log, "device: [%s]", QString(QJsonDocument(inputsDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
return inputsDiscovered;
}

View File

@ -193,7 +193,10 @@ void Hyperion::stop()
void Hyperion::freeObjects()
{
// switch off all leds
//delete Background effect first that it does not kick in when other priorities are stopped
delete _BGEffectHandler;
//Remove all priorities to switch off all leds
clear(-1,true);
// delete components on exit of hyperion core
@ -462,11 +465,6 @@ void Hyperion::setColor(int priority, const std::vector<ColorRgb> &ledColors, in
}
end:
if (getPriorityInfo(priority).componentId != hyperion::COMP_COLOR)
{
clear(priority);
}
// register color
registerInput(priority, hyperion::COMP_COLOR, origin);
@ -618,10 +616,9 @@ void Hyperion::handleVisibleComponentChanged(hyperion::Components comp)
_raw2ledAdjustment->setBacklightEnabled((comp != hyperion::COMP_COLOR && comp != hyperion::COMP_EFFECT));
}
void Hyperion::handleSourceAvailability(const quint8& priority)
void Hyperion::handleSourceAvailability(int priority)
{ int previousPriority = _muxer->getPreviousPriority();
Debug(_log,"priority[%d], previousPriority[%d]", priority, previousPriority);
if ( priority == PriorityMuxer::LOWEST_PRIORITY)
{
Debug(_log,"No source left -> Pause output processing and switch LED-Device off");

View File

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

View File

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

View File

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

View File

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

View File

@ -98,10 +98,14 @@ void QtHttpClientWrapper::onClientDataReceived (void)
if (pos > 0)
{
QByteArray header = raw.left (pos).trimmed ();
QByteArray value = raw.mid (pos +1).trimmed ();
QByteArray header = raw.left (pos).trimmed();
QByteArray value = raw.mid (pos +1).trimmed();
m_currentRequest->addHeader (header, value);
if (header == QtHttpHeader::ContentLength)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0))
if (header.compare(QtHttpHeader::ContentLength, Qt::CaseInsensitive) == 0)
#else
if (header.toLower() == QtHttpHeader::ContentLength.toLower())
#endif
{
bool ok = false;
const int len = value.toInt (&ok, 10);
@ -153,7 +157,7 @@ void QtHttpClientWrapper::onClientDataReceived (void)
case RequestParsed: // a valid request has ben fully parsed
{
// Catch websocket header "Upgrade"
if(m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower() == "websocket")
if(m_currentRequest->getHeader(QtHttpHeader::Upgrade) == "websocket")
{
if(m_websocketClient == Q_NULLPTR)
{
@ -327,7 +331,7 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHtt
{
static const QByteArray & CLOSE = QByteArrayLiteral ("close");
if (m_currentRequest->getHeader (QtHttpHeader::Connection).toLower () == CLOSE)
if (m_currentRequest->getHeader(QtHttpHeader::Connection) == CLOSE)
{
// must close connection after this request
m_sockClient->close ();

View File

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

View File

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

View File

@ -68,7 +68,7 @@ void JsonConnection::setColor(std::vector<QColor> colors, int priority, int dura
parseReply(reply);
}
void JsonConnection::setImage(QImage &image, int priority, int duration)
void JsonConnection::setImage(QImage &image, int priority, int duration, const QString& name)
{
Debug(_log, "Set image has size: %dx%d", image.width(), image.height());
@ -93,6 +93,8 @@ void JsonConnection::setImage(QImage &image, int priority, int duration)
command["command"] = QString("image");
command["priority"] = priority;
command["origin"] = QString("hyperion-remote");
if (!name.isEmpty())
command["name"] = name;
command["imagewidth"] = image.width();
command["imageheight"] = image.height();
command["imagedata"] = QString(base64Image.data());

View File

@ -41,13 +41,14 @@ public:
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 priority The priority
/// @param duration The duration in milliseconds
/// @param name The image's filename
///
void setImage(QImage &image, int priority, int duration);
void setImage(QImage &image, int priority, int duration, const QString& name = "");
#if defined(ENABLE_EFFECTENGINE)
///

View File

@ -252,7 +252,8 @@ int main(int argc, char * argv[])
}
else if (parser.isSet(argImage))
{
connection.setImage(argImage.getImage(parser), argPriority.getInt(parser), argDuration.getInt(parser));
QFileInfo imageFile {argImage.getCString(parser)};
connection.setImage(argImage.getImage(parser), argPriority.getInt(parser), argDuration.getInt(parser), imageFile.fileName());
}
#if defined(ENABLE_EFFECTENGINE)
else if (parser.isSet(argEffect))

View File

@ -103,23 +103,7 @@ QCoreApplication* createApplication(int &argc, char *argv[])
#else
if (!forceNoGui)
{
// if x11, then test if xserver is available
#if defined(ENABLE_X11)
Display* dpy = XOpenDisplay(NULL);
if (dpy != NULL)
{
XCloseDisplay(dpy);
isGuiApp = true;
}
#elif defined(ENABLE_XCB)
int screen_num;
xcb_connection_t * connection = xcb_connect(nullptr, &screen_num);
if (!xcb_connection_has_error(connection))
{
isGuiApp = true;
}
xcb_disconnect(connection);
#endif
isGuiApp = (getenv("DISPLAY") != NULL && (getenv("XDG_SESSION_TYPE") != NULL || getenv("WAYLAND_DISPLAY") != NULL));
}
#endif
@ -397,7 +381,7 @@ int main(int argc, char** argv)
}
}
Info(log,"Starting Hyperion - %s, %s, built: %s:%s", HYPERION_VERSION, HYPERION_BUILD_ID, __DATE__, __TIME__);
Info(log,"Starting Hyperion [%sGUI mode] - %s, %s, built: %s:%s", isGuiApp ? "": "non-", HYPERION_VERSION, HYPERION_BUILD_ID, __DATE__, __TIME__);
Debug(log,"QtVersion [%s]", QT_VERSION_STR);
if ( !readonlyMode )
@ -423,7 +407,7 @@ int main(int argc, char** argv)
// run the application
if (isGuiApp)
{
Info(log, "start systray");
Info(log, "Start Systray menu");
QApplication::setQuitOnLastWindowClosed(false);
SysTray tray(hyperiond);
tray.hide();