mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Introduce Event Services (#1653)
* Allow to enable/disable suspend & lock event handling * Fix Windows * Refactor event handling incl.CEC * Revert "Auxiliary commit to revert individual files from 0d9a8b8a3a4a09609a339f54c7d8a9384c561282" This reverts commit 80737d926ad151a07b2493dd1685ed502975cb2e. * Support Events for Grabbers generically * Have CECEvent to actions configurable, further clean-ups * Remove handleEvent from V4L2grabber, as grabber will be stopped on suspend * Validate that one CEC Event can only trigger one action * MacOS lock/unlock added * fast windows fix * Corrections * Fix CodeQL findings * add macos lock/unlock handler * Migration of CEC-config and have default actions * Correct target_link_libraries * Include Foundation * macOS include AppKit * Support Scheduled Events, cleanups. * Fix destructing * Fix coredump during free * Consider additional error sceanrio * Fix missing code * install desktop icons * correct bash logic --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com>
This commit is contained in:
parent
2e0cc9cfa8
commit
a1f0821f33
@ -12,6 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- New language: Hebrew
|
- New language: Hebrew
|
||||||
|
|
||||||
|
**Event Services**
|
||||||
|
Newly introduced Event Service configuration and consistent handling across all components
|
||||||
|
- Suspend/Resume & Screen Locking support for MaxOS
|
||||||
|
- Allow to enable/disable suspend & lock on operating system events (#1633, #1632)
|
||||||
|
- Scheduled events allowing to suspend,resume, etc. (#1088)
|
||||||
|
- Configurable CEC event handling
|
||||||
|
|
||||||
##### LED-Devices
|
##### LED-Devices
|
||||||
|
|
||||||
**Philips Hue**
|
**Philips Hue**
|
||||||
|
11
assets/webconfig/content/conf_events.html
Normal file
11
assets/webconfig/content/conf_events.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<h3 class="page-header"><i class="fa fa-server fa-fw"></i><span data-i18n="main_menu_events">Event Services</span></h3>
|
||||||
|
<div id="conf_cont">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/content_events.js"></script>
|
@ -192,6 +192,12 @@
|
|||||||
"conf_network_tok_intro": "Here you can create and delete tokens for API authentication. Created tokens will only be displayed once.",
|
"conf_network_tok_intro": "Here you can create and delete tokens for API authentication. Created tokens will only be displayed once.",
|
||||||
"conf_network_tok_lastuse": "Last use",
|
"conf_network_tok_lastuse": "Last use",
|
||||||
"conf_network_tok_title": "Token Management",
|
"conf_network_tok_title": "Token Management",
|
||||||
|
"conf_cec_events_heading_title": "CEC Events",
|
||||||
|
"conf_cec_events_intro": "Settings related to diffent CEC (Consumer Electronics Control) protocol events Hyperion can handle",
|
||||||
|
"conf_os_events_heading_title": "Operating System Events",
|
||||||
|
"conf_os_events_intro": "Settings related to diffent Operating System events Hyperion can handle",
|
||||||
|
"conf_sched_events_heading_title": "Scheduled Events",
|
||||||
|
"conf_sched_events_intro": "Settings related to scheduled, i.e. time based events, which Hyperion will handle",
|
||||||
"conf_webconfig_label_intro": "Webconfiguration settings. Edit wisely.",
|
"conf_webconfig_label_intro": "Webconfiguration settings. Edit wisely.",
|
||||||
"dashboard_active_instance": "Selected instance",
|
"dashboard_active_instance": "Selected instance",
|
||||||
"dashboard_alert_message_confedit": "Your Hyperion configuration has been modified. To apply it, restart Hyperion.",
|
"dashboard_alert_message_confedit": "Your Hyperion configuration has been modified. To apply it, restart Hyperion.",
|
||||||
@ -243,6 +249,30 @@
|
|||||||
"edt_append_pixel": "Pixel",
|
"edt_append_pixel": "Pixel",
|
||||||
"edt_append_s": "s",
|
"edt_append_s": "s",
|
||||||
"edt_append_sdegree": "s/degree",
|
"edt_append_sdegree": "s/degree",
|
||||||
|
"edt_conf_action_title": "Action",
|
||||||
|
"edt_conf_action_expl": "Action to be applied",
|
||||||
|
"edt_conf_action_record_validation_error": "The same event can trigger only one action. Clean up Actions $1",
|
||||||
|
"edt_conf_audio_device_expl": "Selected audio input device",
|
||||||
|
"edt_conf_audio_device_title": "Audio Device",
|
||||||
|
"edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to",
|
||||||
|
"edt_conf_audio_effects_title": "Audio Effects",
|
||||||
|
"edt_conf_audio_effect_enum_vumeter": "VU-Meter",
|
||||||
|
"edt_conf_audio_effect_hotcolor_expl": "Hot Color",
|
||||||
|
"edt_conf_audio_effect_hotcolor_title": "Hot Color",
|
||||||
|
"edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier",
|
||||||
|
"edt_conf_audio_effect_multiplier_title": "Multiplier",
|
||||||
|
"edt_conf_audio_effect_safecolor_expl": "Safe Color",
|
||||||
|
"edt_conf_audio_effect_safecolor_title": "Safe Color",
|
||||||
|
"edt_conf_audio_effect_safevalue_expl": "Safe Threshold",
|
||||||
|
"edt_conf_audio_effect_safevalue_title": "Safe Threshold",
|
||||||
|
"edt_conf_audio_effect_set_defaults": "Reset to default values",
|
||||||
|
"edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100",
|
||||||
|
"edt_conf_audio_effect_tolerance_title": "Tolerance",
|
||||||
|
"edt_conf_audio_effect_warncolor_expl": "Warning Color",
|
||||||
|
"edt_conf_audio_effect_warncolor_title": "Warning Color",
|
||||||
|
"edt_conf_audio_effect_warnvalue_expl": "Warning Threshold",
|
||||||
|
"edt_conf_audio_effect_warnvalue_title": "Warning Threshold",
|
||||||
|
"edt_conf_audio_heading_title": "Audio Capture",
|
||||||
"edt_conf_bb_blurRemoveCnt_expl": "Number of pixels that get removed from the detected border to cut away blur.",
|
"edt_conf_bb_blurRemoveCnt_expl": "Number of pixels that get removed from the detected border to cut away blur.",
|
||||||
"edt_conf_bb_blurRemoveCnt_title": "Blur pixel",
|
"edt_conf_bb_blurRemoveCnt_title": "Blur pixel",
|
||||||
"edt_conf_bb_borderFrameCnt_expl": "Number of frames before a consistent detected border is set.",
|
"edt_conf_bb_borderFrameCnt_expl": "Number of frames before a consistent detected border is set.",
|
||||||
@ -258,6 +288,17 @@
|
|||||||
"edt_conf_bb_unknownFrameCnt_title": "Unknown frames",
|
"edt_conf_bb_unknownFrameCnt_title": "Unknown frames",
|
||||||
"edt_conf_bge_heading_title": "Background Effect/Color",
|
"edt_conf_bge_heading_title": "Background Effect/Color",
|
||||||
"edt_conf_bobls_heading_title": "Boblight Server",
|
"edt_conf_bobls_heading_title": "Boblight Server",
|
||||||
|
"edt_conf_cec_actions_header_title": "Actions",
|
||||||
|
"edt_conf_cec_actions_header_expl": "Define which action should take place on a recognised CEC event",
|
||||||
|
"edt_conf_cec_actions_header_item_title": "Action",
|
||||||
|
"edt_conf_cec_button_release_delay_ms_title": "Button release time",
|
||||||
|
"edt_conf_cec_button_release_delay_ms_expl": "Remote button press release time",
|
||||||
|
"edt_conf_cec_button_repeat_rate_ms_title": "Button repeat rate",
|
||||||
|
"edt_conf_cec_button_repeat_rate_ms_expl": "Remote button press repeat rate",
|
||||||
|
"edt_conf_cec_double_tap_timeout_ms_title": "Button delay before repeating",
|
||||||
|
"edt_conf_cec_double_tap_timeout_ms_expl": "Remote button press delay before repeating",
|
||||||
|
"edt_conf_cec_event_title": "CEC Event",
|
||||||
|
"edt_conf_cec_event_expl": "CEC event that will trigger an action",
|
||||||
"edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.",
|
"edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.",
|
||||||
"edt_conf_color_accuracyLevel_title": "Accuracy level",
|
"edt_conf_color_accuracyLevel_title": "Accuracy level",
|
||||||
"edt_conf_color_backlightColored_expl": "Add some color to your backlight.",
|
"edt_conf_color_backlightColored_expl": "Add some color to your backlight.",
|
||||||
@ -320,6 +361,13 @@
|
|||||||
"edt_conf_enum_HORIZONTAL": "Horizontal",
|
"edt_conf_enum_HORIZONTAL": "Horizontal",
|
||||||
"edt_conf_enum_VERTICAL": "Vertical",
|
"edt_conf_enum_VERTICAL": "Vertical",
|
||||||
"edt_conf_enum_BOTH": "Horizontal & Vertical",
|
"edt_conf_enum_BOTH": "Horizontal & Vertical",
|
||||||
|
"edt_conf_enum_action_idle": "Idle",
|
||||||
|
"edt_conf_enum_action_restart": "Restart",
|
||||||
|
"edt_conf_enum_action_resume": "Resume",
|
||||||
|
"edt_conf_enum_action_resumeIdle": "ResumeIdle",
|
||||||
|
"edt_conf_enum_action_suspend": "Suspend",
|
||||||
|
"edt_conf_enum_action_toggleIdle": "ToggleIdle",
|
||||||
|
"edt_conf_enum_action_toggleSuspend": "ToggleSuspend",
|
||||||
"edt_conf_enum_automatic": "Automatic",
|
"edt_conf_enum_automatic": "Automatic",
|
||||||
"edt_conf_enum_bbclassic": "Classic",
|
"edt_conf_enum_bbclassic": "Classic",
|
||||||
"edt_conf_enum_bbdefault": "Default",
|
"edt_conf_enum_bbdefault": "Default",
|
||||||
@ -328,6 +376,12 @@
|
|||||||
"edt_conf_enum_bgr": "BGR",
|
"edt_conf_enum_bgr": "BGR",
|
||||||
"edt_conf_enum_bottom_up": "Bottom up",
|
"edt_conf_enum_bottom_up": "Bottom up",
|
||||||
"edt_conf_enum_brg": "BRG",
|
"edt_conf_enum_brg": "BRG",
|
||||||
|
"edt_conf_enum_cec_key_f1_blue": "Blue button pressed",
|
||||||
|
"edt_conf_enum_cec_key_f2_red": "Red button pressed",
|
||||||
|
"edt_conf_enum_cec_key_f3_green": "Green button pressed",
|
||||||
|
"edt_conf_enum_cec_key_f4_yellow": "Yellow button pressed",
|
||||||
|
"edt_conf_enum_cec_opcode_set stream path": "TV on",
|
||||||
|
"edt_conf_enum_cec_opcode_standby": "TV off",
|
||||||
"edt_conf_enum_color": "Color",
|
"edt_conf_enum_color": "Color",
|
||||||
"edt_conf_enum_custom": "Custom",
|
"edt_conf_enum_custom": "Custom",
|
||||||
"edt_conf_enum_decay": "Decay",
|
"edt_conf_enum_decay": "Decay",
|
||||||
@ -455,9 +509,18 @@
|
|||||||
"edt_conf_net_localApiAuth_title": "Local API Authentication",
|
"edt_conf_net_localApiAuth_title": "Local API Authentication",
|
||||||
"edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict the access to the API through the internet to certain IP's.",
|
"edt_conf_net_restirctedInternetAccessAPI_expl": "You can restrict the access to the API through the internet to certain IP's.",
|
||||||
"edt_conf_net_restirctedInternetAccessAPI_title": "Restrict to IP's",
|
"edt_conf_net_restirctedInternetAccessAPI_title": "Restrict to IP's",
|
||||||
|
"edt_conf_os_events_lockEnable_title": "Listen to lock events",
|
||||||
|
"edt_conf_os_events_lockEnable_expl": "Listen to screen lock/unlock events",
|
||||||
|
"edt_conf_os_events_suspendEnable_title": "Listen to suspend events",
|
||||||
|
"edt_conf_os_events_suspendEnable_expl": "Listen to operating system suspend/resume events",
|
||||||
|
"edt_conf_os_events_suspendOnLockEnable_title": "Suspend when locked",
|
||||||
|
"edt_conf_os_events_suspendOnLockEnable_expl": "Suspend when the screen is locked, otherwise go into idle mode",
|
||||||
"edt_conf_pbs_heading_title": "Protocol Buffers Server",
|
"edt_conf_pbs_heading_title": "Protocol Buffers Server",
|
||||||
"edt_conf_pbs_timeout_expl": "If no data are received for the given period, the component will be (soft) disabled.",
|
"edt_conf_pbs_timeout_expl": "If no data are received for the given period, the component will be (soft) disabled.",
|
||||||
"edt_conf_pbs_timeout_title": "Timeout",
|
"edt_conf_pbs_timeout_title": "Timeout",
|
||||||
|
"edt_conf_sched_actions_header_title": "Actions",
|
||||||
|
"edt_conf_sched_actions_header_expl": "Define which action should take place on a point in time. The action will be scheduled daily.",
|
||||||
|
"edt_conf_sched_actions_header_item_title": "Action",
|
||||||
"edt_conf_smooth_continuousOutput_expl": "Update the LEDs even there is no changed picture.",
|
"edt_conf_smooth_continuousOutput_expl": "Update the LEDs even there is no changed picture.",
|
||||||
"edt_conf_smooth_continuousOutput_title": "Continuous output",
|
"edt_conf_smooth_continuousOutput_title": "Continuous output",
|
||||||
"edt_conf_smooth_decay_expl": "The speed of decay. 1 is linear, greater values are have stronger effect.",
|
"edt_conf_smooth_decay_expl": "The speed of decay. 1 is linear, greater values are have stronger effect.",
|
||||||
@ -475,6 +538,8 @@
|
|||||||
"edt_conf_smooth_updateDelay_title": "Output delay",
|
"edt_conf_smooth_updateDelay_title": "Output delay",
|
||||||
"edt_conf_smooth_updateFrequency_expl": "The output speed to your LED controller.",
|
"edt_conf_smooth_updateFrequency_expl": "The output speed to your LED controller.",
|
||||||
"edt_conf_smooth_updateFrequency_title": "Update frequency",
|
"edt_conf_smooth_updateFrequency_title": "Update frequency",
|
||||||
|
"edt_conf_time_event_title": "Time",
|
||||||
|
"edt_conf_time_event_expl": "Point in time that will trigger an action",
|
||||||
"edt_conf_v4l2_blueSignalThreshold_expl": "Darkens low blue values (recognized as black)",
|
"edt_conf_v4l2_blueSignalThreshold_expl": "Darkens low blue values (recognized as black)",
|
||||||
"edt_conf_v4l2_blueSignalThreshold_title": "Blue signal threshold",
|
"edt_conf_v4l2_blueSignalThreshold_title": "Blue signal threshold",
|
||||||
"edt_conf_v4l2_cecDetection_expl": "If enabled, USB capture will be temporarily disabled when CEC standby event received from HDMI bus.",
|
"edt_conf_v4l2_cecDetection_expl": "If enabled, USB capture will be temporarily disabled when CEC standby event received from HDMI bus.",
|
||||||
@ -534,27 +599,6 @@
|
|||||||
"edt_conf_v4l2_hardware_set_defaults_tip": "Set device's default values for brightness, contrast, hue and saturation",
|
"edt_conf_v4l2_hardware_set_defaults_tip": "Set device's default values for brightness, contrast, hue and saturation",
|
||||||
"edt_conf_v4l2_noSignalCounterThreshold_title": "Signal Counter Threshold",
|
"edt_conf_v4l2_noSignalCounterThreshold_title": "Signal Counter Threshold",
|
||||||
"edt_conf_v4l2_noSignalCounterThreshold_expl": "Count of frames (check that with grabber's current FPS mode) after which the no signal is triggered",
|
"edt_conf_v4l2_noSignalCounterThreshold_expl": "Count of frames (check that with grabber's current FPS mode) after which the no signal is triggered",
|
||||||
"edt_conf_audio_device_expl": "Selected audio input device",
|
|
||||||
"edt_conf_audio_device_title": "Audio Device",
|
|
||||||
"edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to",
|
|
||||||
"edt_conf_audio_effects_title": "Audio Effects",
|
|
||||||
"edt_conf_audio_effect_enum_vumeter": "VU-Meter",
|
|
||||||
"edt_conf_audio_effect_hotcolor_expl": "Hot Color",
|
|
||||||
"edt_conf_audio_effect_hotcolor_title": "Hot Color",
|
|
||||||
"edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier",
|
|
||||||
"edt_conf_audio_effect_multiplier_title": "Multiplier",
|
|
||||||
"edt_conf_audio_effect_safecolor_expl": "Safe Color",
|
|
||||||
"edt_conf_audio_effect_safecolor_title": "Safe Color",
|
|
||||||
"edt_conf_audio_effect_safevalue_expl": "Safe Threshold",
|
|
||||||
"edt_conf_audio_effect_safevalue_title": "Safe Threshold",
|
|
||||||
"edt_conf_audio_effect_set_defaults": "Reset to default values",
|
|
||||||
"edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100",
|
|
||||||
"edt_conf_audio_effect_tolerance_title": "Tolerance",
|
|
||||||
"edt_conf_audio_effect_warncolor_expl": "Warning Color",
|
|
||||||
"edt_conf_audio_effect_warncolor_title": "Warning Color",
|
|
||||||
"edt_conf_audio_effect_warnvalue_expl": "Warning Threshold",
|
|
||||||
"edt_conf_audio_effect_warnvalue_title": "Warning Threshold",
|
|
||||||
"edt_conf_audio_heading_title": "Audio Capture",
|
|
||||||
"edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)",
|
"edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)",
|
||||||
"edt_conf_webc_crtPath_title": "Certificate path",
|
"edt_conf_webc_crtPath_title": "Certificate path",
|
||||||
"edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)",
|
"edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)",
|
||||||
@ -983,6 +1027,8 @@
|
|||||||
"main_menu_dashboard_token": "Dashboard",
|
"main_menu_dashboard_token": "Dashboard",
|
||||||
"main_menu_effect_conf_token": "Effects",
|
"main_menu_effect_conf_token": "Effects",
|
||||||
"main_menu_effectsconfigurator_token": "Effects Configurator",
|
"main_menu_effectsconfigurator_token": "Effects Configurator",
|
||||||
|
"main_menu_events": "Event Services",
|
||||||
|
"main_menu_event_services_token": "Event Services",
|
||||||
"main_menu_general_conf_token": "General",
|
"main_menu_general_conf_token": "General",
|
||||||
"main_menu_grabber_conf_token": "Capturing Hardware",
|
"main_menu_grabber_conf_token": "Capturing Hardware",
|
||||||
"main_menu_input_selection_token": "Input Selection",
|
"main_menu_input_selection_token": "Input Selection",
|
||||||
|
@ -280,6 +280,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<a class="inactive"><i class="fa fa-industry fa-fw"></i><span data-i18n="main_menu_system_token">System</span><span class="fa arrow"></span></a>
|
<a class="inactive"><i class="fa fa-industry fa-fw"></i><span data-i18n="main_menu_system_token">System</span><span class="fa arrow"></span></a>
|
||||||
<ul class="nav nav-second-level">
|
<ul class="nav nav-second-level">
|
||||||
|
<li> <a class="inactive mnava" id="MenuItemEventServices" href="#conf_events"><i class="fa fa-server fa-fw"></i><span data-i18n="main_menu_event_services_token">Event Services</span></a> </li>
|
||||||
<li> <a class="inactive mnava" id="MenuItemWeb" href="#conf_webconfig" id="load_webconfig"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_webconfig_token">Webconfiguration</span></a> </li>
|
<li> <a class="inactive mnava" id="MenuItemWeb" href="#conf_webconfig" id="load_webconfig"><i class="fa fa-wrench fa-fw"></i><span data-i18n="main_menu_webconfig_token">Webconfiguration</span></a> </li>
|
||||||
<li> <a class="inactive mnava" id="MenuItemLogging" href="#conf_logging"><i class="fa fa-reorder fa-fw"></i><span data-i18n="main_menu_logging_token">Log</span></a> </li>
|
<li> <a class="inactive mnava" id="MenuItemLogging" href="#conf_logging"><i class="fa fa-reorder fa-fw"></i><span data-i18n="main_menu_logging_token">Log</span></a> </li>
|
||||||
<li> <a class="inactive mnava" href="#update"><i class="fa fa-download fa-fw"></i><span data-i18n="main_menu_update_token">Update</span></a> </li>
|
<li> <a class="inactive mnava" href="#update"><i class="fa fa-download fa-fw"></i><span data-i18n="main_menu_update_token">Update</span></a> </li>
|
||||||
|
157
assets/webconfig/js/content_events.js
Normal file
157
assets/webconfig/js/content_events.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
$(document).ready(function () {
|
||||||
|
performTranslation();
|
||||||
|
|
||||||
|
const CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1);
|
||||||
|
|
||||||
|
let conf_editor_osEvents = null;
|
||||||
|
let conf_editor_cecEvents = null;
|
||||||
|
let conf_editor_schedEvents = null;
|
||||||
|
|
||||||
|
if (window.showOptHelp) {
|
||||||
|
//Operating System Events
|
||||||
|
$('#conf_cont').append(createRow('conf_cont_os_events'));
|
||||||
|
$('#conf_cont_os_events').append(createOptPanel('fa-laptop', $.i18n("conf_os_events_heading_title"), 'editor_container_os_events', 'btn_submit_os_events', 'panel-system'));
|
||||||
|
$('#conf_cont_os_events').append(createHelpTable(window.schema.osEvents.properties, $.i18n("conf_os_events_heading_title")));
|
||||||
|
|
||||||
|
//Scheduled Events
|
||||||
|
$('#conf_cont').append(createRow('conf_cont_sched_events'));
|
||||||
|
$('#conf_cont_sched_events').append(createOptPanel('fa-laptop', $.i18n("conf_sched_events_heading_title"), 'editor_container_sched_events', 'btn_submit_sched_events', 'panel-system'));
|
||||||
|
$('#conf_cont_sched_events').append(createHelpTable(window.schema.schedEvents.properties, $.i18n("conf_sched_events_heading_title")));
|
||||||
|
|
||||||
|
|
||||||
|
//CEC Events
|
||||||
|
if (CEC_ENABLED) {
|
||||||
|
$('#conf_cont').append(createRow('conf_cont_event_cec'));
|
||||||
|
$('#conf_cont_event_cec').append(createOptPanel('fa-tv', $.i18n("conf_cec_events_heading_title"), 'editor_container_cec_events', 'btn_submit_cec_events', 'panel-system'));
|
||||||
|
$('#conf_cont_event_cec').append(createHelpTable(window.schema.cecEvents.properties, $.i18n("conf_cec_events_heading_title"), "cecEventsHelpPanelId"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#conf_cont').addClass('row');
|
||||||
|
$('#conf_cont').append(createOptPanel('fa-laptop', $.i18n("conf_os_events_heading_title"), 'editor_container_os_events', 'btn_submit_os_events'));
|
||||||
|
$('#conf_cont').append(createOptPanel('fa-laptop', $.i18n("conf_sched_events_heading_title"), 'editor_container_sched_events', 'btn_submit_sched_events'));
|
||||||
|
if (CEC_ENABLED) {
|
||||||
|
$('#conf_cont').append(createOptPanel('fa-tv', $.i18n("conf_cec_events_heading_title"), 'editor_container_cec_events', 'btn_submit_cec_events'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findDuplicateEventsIndices(data) {
|
||||||
|
const eventIndices = {};
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
const event = item.event;
|
||||||
|
if (!eventIndices[event]) {
|
||||||
|
eventIndices[event] = [index];
|
||||||
|
} else {
|
||||||
|
eventIndices[event].push(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.values(eventIndices).filter(indices => indices.length > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONEditor.defaults.custom_validators.push(function (schema, value, path) {
|
||||||
|
let errors = [];
|
||||||
|
if (schema.type === 'array' && Array.isArray(value)) {
|
||||||
|
const duplicateEventIndices = findDuplicateEventsIndices(value);
|
||||||
|
|
||||||
|
if (duplicateEventIndices.length > 0) {
|
||||||
|
|
||||||
|
let recs;
|
||||||
|
duplicateEventIndices.forEach(indices => {
|
||||||
|
const displayIndices = indices.map(index => index + 1);
|
||||||
|
recs = displayIndices.join(', ');
|
||||||
|
});
|
||||||
|
|
||||||
|
errors.push({
|
||||||
|
path: path,
|
||||||
|
message: $.i18n('edt_conf_action_record_validation_error', recs)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
});
|
||||||
|
|
||||||
|
//Operating System Events
|
||||||
|
conf_editor_osEvents = createJsonEditor('editor_container_os_events', {
|
||||||
|
osEvents: window.schema.osEvents
|
||||||
|
}, true, true);
|
||||||
|
|
||||||
|
conf_editor_osEvents.on('change', function () {
|
||||||
|
conf_editor_osEvents.validate().length || window.readOnlyMode ? $('#btn_submit_os_events').prop('disabled', true) : $('#btn_submit_os_events').prop('disabled', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_submit_os_events').off().on('click', function () {
|
||||||
|
requestWriteConfig(conf_editor_osEvents.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
//Scheduled Events
|
||||||
|
conf_editor_schedEvents = createJsonEditor('editor_container_sched_events', {
|
||||||
|
schedEvents: window.schema.schedEvents
|
||||||
|
}, true, true);
|
||||||
|
|
||||||
|
conf_editor_schedEvents.on('change', function () {
|
||||||
|
|
||||||
|
const schedEventsEnable = conf_editor_schedEvents.getEditor("root.schedEvents.enable").getValue();
|
||||||
|
|
||||||
|
if (schedEventsEnable) {
|
||||||
|
showInputOptionsForKey(conf_editor_schedEvents, "schedEvents", "enable", true);
|
||||||
|
$('#schedEventsHelpPanelId').show();
|
||||||
|
} else {
|
||||||
|
showInputOptionsForKey(conf_editor_schedEvents, "schedEvents", "enable", false);
|
||||||
|
$('#schedEventsHelpPanelId').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
conf_editor_schedEvents.validate().length || window.readOnlyMode ? $('#btn_submit_sched_events').prop('disabled', true) : $('#btn_submit_sched_events').prop('disabled', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_submit_sched_events').off().on('click', function () {
|
||||||
|
|
||||||
|
const saveOptions = conf_editor_schedEvents.getValue();
|
||||||
|
// Workaround, as otherwise values are not reflected correctly
|
||||||
|
saveOptions.schedEvents.enable = conf_editor_schedEvents.getEditor("root.schedEvents.enable").getValue();
|
||||||
|
saveOptions.schedEvents.actions = conf_editor_schedEvents.getEditor("root.schedEvents.actions").getValue();
|
||||||
|
requestWriteConfig(saveOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
//CEC Events
|
||||||
|
if (CEC_ENABLED) {
|
||||||
|
conf_editor_cecEvents = createJsonEditor('editor_container_cec_events', {
|
||||||
|
cecEvents: window.schema.cecEvents
|
||||||
|
}, true, true);
|
||||||
|
|
||||||
|
conf_editor_cecEvents.on('change', function () {
|
||||||
|
|
||||||
|
const cecEventsEnable = conf_editor_cecEvents.getEditor("root.cecEvents.enable").getValue();
|
||||||
|
|
||||||
|
if (cecEventsEnable) {
|
||||||
|
showInputOptionsForKey(conf_editor_cecEvents, "cecEvents", "enable", true);
|
||||||
|
$('#cecEventsHelpPanelId').show();
|
||||||
|
} else {
|
||||||
|
showInputOptionsForKey(conf_editor_cecEvents, "cecEvents", "enable", false);
|
||||||
|
$('#cecEventsHelpPanelId').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
conf_editor_cecEvents.validate().length || window.readOnlyMode ? $('#btn_submit_cec_events').prop('disabled', true) : $('#btn_submit_cec_events').prop('disabled', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btn_submit_cec_events').off().on('click', function () {
|
||||||
|
|
||||||
|
const saveOptions = conf_editor_cecEvents.getValue();
|
||||||
|
// Workaround, as otherwise values are not reflected correctly
|
||||||
|
saveOptions.cecEvents.enable = conf_editor_cecEvents.getEditor("root.cecEvents.enable").getValue();
|
||||||
|
saveOptions.cecEvents.actions = conf_editor_cecEvents.getEditor("root.cecEvents.actions").getValue();
|
||||||
|
requestWriteConfig(saveOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//create introduction
|
||||||
|
if (window.showOptHelp) {
|
||||||
|
createHint("intro", $.i18n('conf_os_events_intro'), "editor_container_os_events");
|
||||||
|
if (CEC_ENABLED) {
|
||||||
|
createHint("intro", $.i18n('conf_cec_events_intro'), "editor_container_cec_events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeOverlay();
|
||||||
|
});
|
||||||
|
|
@ -5,7 +5,6 @@ $(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);
|
||||||
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
|
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
|
||||||
var CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1);
|
|
||||||
|
|
||||||
var conf_editor_video = null;
|
var conf_editor_video = null;
|
||||||
var conf_editor_audio = null;
|
var conf_editor_audio = null;
|
||||||
@ -369,11 +368,6 @@ $(document).ready(function () {
|
|||||||
|
|
||||||
conf_editor_video.on('change', function () {
|
conf_editor_video.on('change', function () {
|
||||||
|
|
||||||
// Hide elements not supported by the backend
|
|
||||||
if (window.serverInfo.cec.enabled === false || !CEC_ENABLED) {
|
|
||||||
showInputOptionForItem(conf_editor_video, "grabberV4L2", "cecDetection", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the current editor's content
|
// Validate the current editor's content
|
||||||
if (!conf_editor_video.validate().length) {
|
if (!conf_editor_video.validate().length) {
|
||||||
var deviceSelected = conf_editor_video.getEditor("root.grabberV4L2.available_devices").getValue();
|
var deviceSelected = conf_editor_video.getEditor("root.grabberV4L2.available_devices").getValue();
|
||||||
|
@ -140,7 +140,7 @@ for i in data:
|
|||||||
|
|
||||||
if [ -z "$head_sha" ]; then
|
if [ -z "$head_sha" ]; then
|
||||||
echo "---> The specified PR #$pr_number has no longer any artifacts or has been closed."
|
echo "---> The specified PR #$pr_number has no longer any artifacts or has been closed."
|
||||||
echo "---> It may be older than 14 days. Ask the PR creator to recreate the artifacts at the following URL:"
|
echo "---> It may be older than 14 days or a new build currently in progress. Ask the PR creator to recreate the artifacts at the following URL:"
|
||||||
echo "---> https://github.com/hyperion-project/hyperion.ng/pull/$pr_number"
|
echo "---> https://github.com/hyperion-project/hyperion.ng/pull/$pr_number"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -103,11 +103,23 @@ ln -fs $BINSP/scripts/updateHyperionUser.sh $BINTP/updateHyperionUser 2>/dev/nul
|
|||||||
|
|
||||||
# install desktop icons (only on initial installation and not upgrade)
|
# install desktop icons (only on initial installation and not upgrade)
|
||||||
if [ "$IS_UPGRADE" = false ]; then
|
if [ "$IS_UPGRADE" = false ]; then
|
||||||
if hash desktop-file-install 2>/dev/null; then
|
if [ ! -z "${DISPLAY}" ] || [ ! -z "${WAYLAND_DISPLAY}" ] || [ ! -z "${XDG_CURRENT_DESKTOP}" ]; then
|
||||||
echo "---> Install Hyperion desktop icons"
|
echo "---> Install Hyperion desktop icons"
|
||||||
cp -R /usr/share/hyperion/icons /usr/share/icons/hicolor 2>/dev/null
|
cp -R /usr/share/hyperion/icons /usr/share/icons/hicolor 2>/dev/null
|
||||||
cp /usr/share/hyperion/desktop/hyperion.metainfo.xml /usr/share/metainfo/hyperion.metainfo.xml 2>/dev/null
|
cp /usr/share/hyperion/desktop/hyperion.metainfo.xml /usr/share/metainfo 2>/dev/null
|
||||||
desktop-file-install /usr/share/hyperion/desktop/hyperion.desktop 2>/dev/null
|
|
||||||
|
if hash desktop-file-install 2>/dev/null; then
|
||||||
|
desktop-file-install /usr/share/hyperion/desktop/hyperion.desktop 2>/dev/null
|
||||||
|
else
|
||||||
|
# On some systems this directory doesn't exist by default
|
||||||
|
mkdir -p /usr/share/applications
|
||||||
|
cp /usr/share/hyperion/desktop/hyperion.desktop /usr/share/applications
|
||||||
|
# Update application icons
|
||||||
|
if hash update-desktop-database 2>/dev/null; then
|
||||||
|
update-desktop-database -q /usr/share/applications
|
||||||
|
update-desktop-database -q /usr/share/icons/
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ if [ -x /usr/bin/update-desktop-database ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# remove Hyperion icons
|
# remove Hyperion icons
|
||||||
for i in 16x16 22x22 24x24 32x32 36x36 48x48 64x64 72x72 96x96 128x128 192x192 256x256 512x512
|
for i in 16x16 22x22 24x24 32x32 36x36 48x48 64x64 72x72 96x96 128x128 192x192 256x256 512x512; do
|
||||||
rm -v usr/share/icons/hicolor/$i/apps/hyperion.png 2> /dev/null
|
rm -v usr/share/icons/hicolor/$i/apps/hyperion.png 2> /dev/null
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -22,6 +22,24 @@
|
|||||||
"enableAttemptsInterval": 15
|
"enableAttemptsInterval": 15
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"schedEvents": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"cecEvents": {
|
||||||
|
"actions": [
|
||||||
|
{
|
||||||
|
"action": "Suspend",
|
||||||
|
"event": "standby"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "Resume",
|
||||||
|
"event": "set stream path"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
|
||||||
"color": {
|
"color": {
|
||||||
"imageToLedMappingType": "multicolor_mean",
|
"imageToLedMappingType": "multicolor_mean",
|
||||||
"channelAdjustment": [
|
"channelAdjustment": [
|
||||||
@ -248,5 +266,14 @@
|
|||||||
"vmax": 0.08,
|
"vmax": 0.08,
|
||||||
"vmin": 0
|
"vmin": 0
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
|
||||||
|
"osEvents": {
|
||||||
|
"suspendEnable": true,
|
||||||
|
"lockEnable": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"cecEvents": {
|
||||||
|
"enable": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
// parent class
|
// parent class
|
||||||
#include <api/API.h>
|
#include <api/API.h>
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
// hyperion includes
|
// hyperion includes
|
||||||
#include <utils/Components.h>
|
#include <utils/Components.h>
|
||||||
@ -105,24 +106,9 @@ signals:
|
|||||||
void forwardJsonMessage(QJsonObject);
|
void forwardJsonMessage(QJsonObject);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Signal emits whenever a suspend/resume request for all instances should be forwarded
|
/// Signal emits whenever a hyperion event request for all instances should be forwarded
|
||||||
///
|
///
|
||||||
void suspendAll(bool isSuspend);
|
void signalEvent(Event event);
|
||||||
|
|
||||||
///
|
|
||||||
/// Signal emits whenever a toggle suspend/resume request for all instances should be forwarded
|
|
||||||
///
|
|
||||||
void toggleSuspendAll();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Signal emits whenever a idle mode request for all instances should be forwarded
|
|
||||||
///
|
|
||||||
void idleAll(bool isIdle);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Signal emits whenever a toggle idle/working mode request for all instances should be forwarded
|
|
||||||
///
|
|
||||||
void toggleIdleAll();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// true if further callbacks are forbidden (http)
|
// true if further callbacks are forbidden (http)
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
enum class CECEvent
|
|
||||||
{
|
|
||||||
On,
|
|
||||||
Off
|
|
||||||
};
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <libcec/cec.h>
|
#include <libcec/cec.h>
|
||||||
|
|
||||||
#include <cec/CECEvent.h>
|
#include <utils/settings.h>
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
using CECCallbacks = CEC::ICECCallbacks;
|
using CECCallbacks = CEC::ICECCallbacks;
|
||||||
using CECAdapter = CEC::ICECAdapter;
|
using CECAdapter = CEC::ICECAdapter;
|
||||||
@ -30,19 +32,28 @@ class CECHandler : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
CECHandler();
|
CECHandler(const QJsonDocument& config, QObject * parent = nullptr);
|
||||||
~CECHandler() override;
|
~CECHandler() override;
|
||||||
|
|
||||||
QString scan() const;
|
QString scan() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
bool start();
|
bool start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
signals:
|
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
||||||
void cecEvent(CECEvent event);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void signalEvent(Event event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool enable();
|
||||||
|
void disable();
|
||||||
|
|
||||||
/* CEC Callbacks */
|
/* CEC Callbacks */
|
||||||
static void onCecLogMessage (void * context, const CECLogMessage * message);
|
static void onCecLogMessage (void * context, const CECLogMessage * message);
|
||||||
static void onCecKeyPress (void * context, const CECKeyPress * key);
|
static void onCecKeyPress (void * context, const CECKeyPress * key);
|
||||||
@ -57,6 +68,11 @@ private:
|
|||||||
bool openAdapter(const CECAdapterDescriptor & descriptor);
|
bool openAdapter(const CECAdapterDescriptor & descriptor);
|
||||||
void printAdapter(const CECAdapterDescriptor & descriptor) const;
|
void printAdapter(const CECAdapterDescriptor & descriptor) const;
|
||||||
|
|
||||||
|
// CEC Event Strings per https://github.com/Pulse-Eight/libcec/blob/master/src/libcec/CECTypeUtils.h
|
||||||
|
void triggerAction(const QString& cecEvent);
|
||||||
|
|
||||||
|
QJsonDocument _config;
|
||||||
|
|
||||||
/* CEC Helpers */
|
/* CEC Helpers */
|
||||||
CECCallbacks getCallbacks() const;
|
CECCallbacks getCallbacks() const;
|
||||||
CECConfig getConfig() const;
|
CECConfig getConfig() const;
|
||||||
@ -65,5 +81,14 @@ private:
|
|||||||
CECCallbacks _cecCallbacks {};
|
CECCallbacks _cecCallbacks {};
|
||||||
CECConfig _cecConfig {};
|
CECConfig _cecConfig {};
|
||||||
|
|
||||||
|
bool _isInitialised;
|
||||||
|
bool _isEnabled;
|
||||||
|
|
||||||
|
int _buttonReleaseDelayMs;
|
||||||
|
int _buttonRepeatRateMs;
|
||||||
|
int _doubleTapTimeoutMs;
|
||||||
|
|
||||||
|
QMap<QString,Event> _cecEventActionMap;
|
||||||
|
|
||||||
Logger * _logger {};
|
Logger * _logger {};
|
||||||
};
|
};
|
||||||
|
@ -112,6 +112,8 @@ public:
|
|||||||
list << "jsonServer" << "protoServer" << "flatbufServer" << "forwarder" << "webConfig" << "network"
|
list << "jsonServer" << "protoServer" << "flatbufServer" << "forwarder" << "webConfig" << "network"
|
||||||
// capture
|
// capture
|
||||||
<< "framegrabber" << "grabberV4L2" << "grabberAudio"
|
<< "framegrabber" << "grabberV4L2" << "grabberAudio"
|
||||||
|
//Events
|
||||||
|
<< "osEvents" << "cecEvents" << "schedEvents"
|
||||||
// other
|
// other
|
||||||
<< "logger" << "general";
|
<< "logger" << "general";
|
||||||
|
|
||||||
|
50
include/events/EventEnum.h
Normal file
50
include/events/EventEnum.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef EVENTENUM_H
|
||||||
|
#define EVENTENUM_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
enum class Event
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Suspend,
|
||||||
|
Resume,
|
||||||
|
ToggleSuspend,
|
||||||
|
Idle,
|
||||||
|
ResumeIdle,
|
||||||
|
ToggleIdle,
|
||||||
|
Reload,
|
||||||
|
Restart
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const char* eventToString(Event event)
|
||||||
|
{
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case Event::Suspend: return "Suspend";
|
||||||
|
case Event::Resume: return "Resume";
|
||||||
|
case Event::ToggleSuspend: return "ToggleSuspend";
|
||||||
|
case Event::Idle: return "Idle";
|
||||||
|
case Event::ResumeIdle: return "ResumeIdle";
|
||||||
|
case Event::ToggleIdle: return "ToggleIdle";
|
||||||
|
case Event::Reload: return "Reload";
|
||||||
|
case Event::Restart: return "Restart";
|
||||||
|
case Event::Unknown:
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Event stringToEvent(const QString& event)
|
||||||
|
{
|
||||||
|
if (event.compare("Suspend")==0) return Event::Suspend;
|
||||||
|
if (event.compare("Resume")==0) return Event::Resume;
|
||||||
|
if (event.compare("ToggleSuspend")==0) return Event::ToggleSuspend;
|
||||||
|
if (event.compare("Idle")==0) return Event::Idle;
|
||||||
|
if (event.compare("ResumeIdle")==0) return Event::ResumeIdle;
|
||||||
|
if (event.compare("ToggleIdle")==0) return Event::ToggleIdle;
|
||||||
|
if (event.compare("Reload")==0) return Event::Reload;
|
||||||
|
if (event.compare("Restart")==0) return Event::Restart;
|
||||||
|
return Event::Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // EVENTENUM_H
|
49
include/events/EventHandler.h
Normal file
49
include/events/EventHandler.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef EVENTHANDLER_H
|
||||||
|
#define EVENTHANDLER_H
|
||||||
|
|
||||||
|
#include <utils/settings.h>
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class Logger;
|
||||||
|
|
||||||
|
class EventHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
EventHandler();
|
||||||
|
~EventHandler() override;
|
||||||
|
|
||||||
|
static EventHandler* getInstance();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void suspend(bool sleep);
|
||||||
|
|
||||||
|
void suspend();
|
||||||
|
void resume();
|
||||||
|
void toggleSuspend();
|
||||||
|
|
||||||
|
void idle(bool isIdle);
|
||||||
|
void idle();
|
||||||
|
void resumeIdle();
|
||||||
|
void toggleIdle();
|
||||||
|
|
||||||
|
void handleEvent(Event event);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void signalEvent(Event event);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Logger * _log {};
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _isSuspended;
|
||||||
|
bool _isIdle;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // EVENTHANDLER_H
|
||||||
|
|
55
include/events/EventScheduler.h
Normal file
55
include/events/EventScheduler.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#ifndef EVENTSCHEDULER_H
|
||||||
|
#define EVENTSCHEDULER_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
#include <utils/settings.h>
|
||||||
|
|
||||||
|
class Logger;
|
||||||
|
|
||||||
|
class EventScheduler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
EventScheduler();
|
||||||
|
~EventScheduler() override;
|
||||||
|
|
||||||
|
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void signalEvent(Event event);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void handleEvent(int timerIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct timeEvent
|
||||||
|
{
|
||||||
|
QTime time;
|
||||||
|
Event action;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool enable();
|
||||||
|
void disable();
|
||||||
|
|
||||||
|
int getMillisecondsToNextScheduledTime(const QTime& time);
|
||||||
|
|
||||||
|
void clearTimers();
|
||||||
|
|
||||||
|
QJsonDocument _config;
|
||||||
|
|
||||||
|
bool _isEnabled;
|
||||||
|
|
||||||
|
QList<timeEvent> _scheduledEvents;
|
||||||
|
QList<QTimer*> _timers;
|
||||||
|
|
||||||
|
Logger * _log {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EVENTSCHEDULER_H
|
132
include/events/OsEventHandler.h
Normal file
132
include/events/OsEventHandler.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#ifndef OSEVENTHANDLER_H
|
||||||
|
#define OSEVENTHANDLER_H
|
||||||
|
#include <QObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <QAbstractNativeEventFilter>
|
||||||
|
#include <QAbstractEventDispatcher>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <utils/settings.h>
|
||||||
|
|
||||||
|
class Logger;
|
||||||
|
|
||||||
|
class OsEventHandlerBase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
OsEventHandlerBase();
|
||||||
|
~OsEventHandlerBase() override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void suspend(bool sleep);
|
||||||
|
void lock(bool isLocked);
|
||||||
|
|
||||||
|
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void signalEvent(Event event);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool registerOsEventHandler() { return true; }
|
||||||
|
virtual void unregisterOsEventHandler() {}
|
||||||
|
virtual bool registerLockHandler() { return true; }
|
||||||
|
virtual void unregisterLockHandler() {}
|
||||||
|
|
||||||
|
bool _isSuspendEnabled;
|
||||||
|
bool _isLockEnabled;
|
||||||
|
bool _isSuspendOnLock;
|
||||||
|
|
||||||
|
bool _isSuspendRegistered;
|
||||||
|
bool _isLockRegistered;
|
||||||
|
|
||||||
|
Logger * _log {};
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
class OsEventHandlerWindows : public OsEventHandlerBase, public QAbstractNativeEventFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
OsEventHandlerWindows();
|
||||||
|
~OsEventHandlerWindows() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
bool nativeEventFilter(const QByteArray& eventType, void* message, qintptr* result) override;
|
||||||
|
#else
|
||||||
|
bool nativeEventFilter(const QByteArray& eventType, void* message, long int* result) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool registerOsEventHandler() override;
|
||||||
|
void unregisterOsEventHandler() override;
|
||||||
|
bool registerLockHandler() override;
|
||||||
|
void unregisterLockHandler() override;
|
||||||
|
|
||||||
|
QWidget _widget;
|
||||||
|
HPOWERNOTIFY _notifyHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
using OsEventHandler = OsEventHandlerWindows;
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
class OsEventHandlerLinux : public OsEventHandlerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
static void static_signaleHandler(int signum)
|
||||||
|
{
|
||||||
|
OsEventHandlerLinux::getInstance()->handleSignal(signum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
OsEventHandlerLinux();
|
||||||
|
|
||||||
|
void handleSignal (int signum);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static OsEventHandlerLinux* getInstance();
|
||||||
|
|
||||||
|
#if defined(HYPERION_HAS_DBUS)
|
||||||
|
bool registerOsEventHandler() override;
|
||||||
|
void unregisterOsEventHandler() override;
|
||||||
|
bool registerLockHandler() override;
|
||||||
|
void unregisterLockHandler() override;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
using OsEventHandler = OsEventHandlerLinux;
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
class OsEventHandlerMacOS : public OsEventHandlerBase
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
OsEventHandlerMacOS();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool registerOsEventHandler() override;
|
||||||
|
void unregisterOsEventHandler() override;
|
||||||
|
bool registerLockHandler() override;
|
||||||
|
void unregisterLockHandler() override;
|
||||||
|
|
||||||
|
void *_sleepEventHandler;
|
||||||
|
void *_lockEventHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
using OsEventHandler = OsEventHandlerMacOS;
|
||||||
|
|
||||||
|
#else
|
||||||
|
using OsEventHandler = OsEventHandlerBase;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // OSEVENTHANDLER_H
|
@ -9,10 +9,6 @@
|
|||||||
#include <grabber/video/v4l2/V4L2Grabber.h>
|
#include <grabber/video/v4l2/V4L2Grabber.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(ENABLE_CEC)
|
|
||||||
#include <cec/CECEvent.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class VideoWrapper : public GrabberWrapper
|
class VideoWrapper : public GrabberWrapper
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -25,10 +21,6 @@ public slots:
|
|||||||
bool start() override;
|
bool start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
#if defined(ENABLE_CEC)
|
|
||||||
void handleCecEvent(CECEvent event);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void handleSettingsUpdate(settings::type type, const QJsonDocument& config) override;
|
void handleSettingsUpdate(settings::type type, const QJsonDocument& config) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -24,9 +24,7 @@
|
|||||||
// Determine the cmake options
|
// Determine the cmake options
|
||||||
#include <HyperionConfig.h>
|
#include <HyperionConfig.h>
|
||||||
|
|
||||||
#if defined(ENABLE_CEC)
|
#include <events/EventEnum.h>
|
||||||
#include <cec/CECEvent.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Capture class for V4L2 devices
|
/// Capture class for V4L2 devices
|
||||||
@ -77,13 +75,10 @@ public:
|
|||||||
void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold = 50);
|
void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold = 50);
|
||||||
void setSignalDetectionOffset( double verticalMin, double horizontalMin, double verticalMax, double horizontalMax);
|
void setSignalDetectionOffset( double verticalMin, double horizontalMin, double verticalMax, double horizontalMax);
|
||||||
void setSignalDetectionEnable(bool enable);
|
void setSignalDetectionEnable(bool enable);
|
||||||
void setCecDetectionEnable(bool enable);
|
|
||||||
bool reload(bool force = false);
|
bool reload(bool force = false);
|
||||||
|
|
||||||
QRectF getSignalDetectionOffset() const { return QRectF(_x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max); } //used from hyperion-v4l2
|
QRectF getSignalDetectionOffset() const { return QRectF(_x_frac_min, _y_frac_min, _x_frac_max, _y_frac_max); } //used from hyperion-v4l2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Discover available V4L2 USB devices (for configuration).
|
/// @brief Discover available V4L2 USB devices (for configuration).
|
||||||
/// @param[in] params Parameters used to overwrite discovery default behaviour
|
/// @param[in] params Parameters used to overwrite discovery default behaviour
|
||||||
@ -97,10 +92,6 @@ public slots:
|
|||||||
void stop();
|
void stop();
|
||||||
void newThreadFrame(Image<ColorRgb> image);
|
void newThreadFrame(Image<ColorRgb> image);
|
||||||
|
|
||||||
#if defined(ENABLE_CEC)
|
|
||||||
void handleCecEvent(CECEvent event);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newFrame(const Image<ColorRgb> & image);
|
void newFrame(const Image<ColorRgb> & image);
|
||||||
void readError(const char* err);
|
void readError(const char* err);
|
||||||
@ -167,7 +158,7 @@ private:
|
|||||||
// signal detection
|
// signal detection
|
||||||
int _noSignalCounterThreshold;
|
int _noSignalCounterThreshold;
|
||||||
ColorRgb _noSignalThresholdColor;
|
ColorRgb _noSignalThresholdColor;
|
||||||
bool _cecDetectionEnabled, _cecStandbyActivated, _signalDetectionEnabled, _noSignalDetected;
|
bool _standbyActivated, _signalDetectionEnabled, _noSignalDetected;
|
||||||
int _noSignalCounter;
|
int _noSignalCounter;
|
||||||
int _brightness, _contrast, _saturation, _hue;
|
int _brightness, _contrast, _saturation, _hue;
|
||||||
double _x_frac_min;
|
double _x_frac_min;
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include <utils/Logger.h>
|
#include <utils/Logger.h>
|
||||||
#include <utils/Components.h>
|
#include <utils/Components.h>
|
||||||
|
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler)
|
/// @brief The Grabber class is responsible to apply image resizes (with or without ImageResampler)
|
||||||
|
|
||||||
@ -111,6 +113,10 @@ public:
|
|||||||
|
|
||||||
QString getGrabberName() const { return _grabberName; }
|
QString getGrabberName() const { return _grabberName; }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
virtual void handleEvent(Event event) {}
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
///
|
///
|
||||||
/// @brief Set device in error state
|
/// @brief Set device in error state
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#include <grabber/GrabberType.h>
|
#include <grabber/GrabberType.h>
|
||||||
|
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
class Grabber;
|
class Grabber;
|
||||||
class GlobalSignals;
|
class GlobalSignals;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
@ -139,6 +141,8 @@ public slots:
|
|||||||
///
|
///
|
||||||
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
||||||
|
|
||||||
|
void handleEvent(Event event);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
///
|
///
|
||||||
/// @brief Emit the final processed image
|
/// @brief Emit the final processed image
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <utils/VideoMode.h>
|
#include <utils/VideoMode.h>
|
||||||
#include <utils/settings.h>
|
#include <utils/settings.h>
|
||||||
#include <utils/Components.h>
|
#include <utils/Components.h>
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
// qt
|
// qt
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
@ -74,20 +75,10 @@ public slots:
|
|||||||
bool stopInstance(quint8 inst);
|
bool stopInstance(quint8 inst);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Suspend (disable) all Hyperion instances
|
/// @brief Handle an Hyperion Event
|
||||||
|
/// @param event Event to be processed
|
||||||
///
|
///
|
||||||
void suspend();
|
void handleEvent(Event event);
|
||||||
|
|
||||||
///
|
|
||||||
/// @brief Resume (resume) all Hyperion instances
|
|
||||||
///
|
|
||||||
void resume();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// @brief Toggle the state of all Hyperion instances for an idle sceanrio (user is not interacting with the system
|
|
||||||
/// @param isIdle, If true all instances toggle to idle, else to resume
|
|
||||||
///
|
|
||||||
void toggleIdle(bool isIdle);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Toggle the state of all Hyperion instances
|
/// @brief Toggle the state of all Hyperion instances
|
||||||
@ -141,10 +132,6 @@ signals:
|
|||||||
///
|
///
|
||||||
void startInstanceResponse(QObject *caller, const int &tan);
|
void startInstanceResponse(QObject *caller, const int &tan);
|
||||||
|
|
||||||
void triggerSuspend(bool isSuspend);
|
|
||||||
void triggerToggleSuspend();
|
|
||||||
void triggerIdle(bool isIdle);
|
|
||||||
void triggerToggleIdle();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
@ -186,6 +173,18 @@ private slots:
|
|||||||
///
|
///
|
||||||
void handleFinished();
|
void handleFinished();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Toggle the state of all Hyperion instances for a suspend sceanrio (user is not interacting with the system)
|
||||||
|
/// @param isSuspend, If true all instances toggle to suspend, else to resume
|
||||||
|
///
|
||||||
|
void toggleSuspend(bool isSuspend);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @brief Toggle the state of all Hyperion instances for an idle sceanrio
|
||||||
|
/// @param isIdle, If true all instances toggle to idle, else to resume
|
||||||
|
///
|
||||||
|
void toggleIdle(bool isIdle);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class HyperionDaemon;
|
friend class HyperionDaemon;
|
||||||
///
|
///
|
||||||
|
@ -30,6 +30,9 @@ namespace settings {
|
|||||||
NETWORK,
|
NETWORK,
|
||||||
FLATBUFSERVER,
|
FLATBUFSERVER,
|
||||||
PROTOSERVER,
|
PROTOSERVER,
|
||||||
|
OSEVENTS,
|
||||||
|
CECEVENTS,
|
||||||
|
SCHEDEVENTS,
|
||||||
INVALID
|
INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,29 +45,32 @@ namespace settings {
|
|||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case BGEFFECT: return "backgroundEffect";
|
case BGEFFECT: return "backgroundEffect";
|
||||||
case FGEFFECT: return "foregroundEffect";
|
case FGEFFECT: return "foregroundEffect";
|
||||||
case BLACKBORDER: return "blackborderdetector";
|
case BLACKBORDER: return "blackborderdetector";
|
||||||
case BOBLSERVER: return "boblightServer";
|
case BOBLSERVER: return "boblightServer";
|
||||||
case COLOR: return "color";
|
case COLOR: return "color";
|
||||||
case DEVICE: return "device";
|
case DEVICE: return "device";
|
||||||
case EFFECTS: return "effects";
|
case EFFECTS: return "effects";
|
||||||
case NETFORWARD: return "forwarder";
|
case NETFORWARD: return "forwarder";
|
||||||
case SYSTEMCAPTURE: return "framegrabber";
|
case SYSTEMCAPTURE: return "framegrabber";
|
||||||
case GENERAL: return "general";
|
case GENERAL: return "general";
|
||||||
case V4L2: return "grabberV4L2";
|
case V4L2: return "grabberV4L2";
|
||||||
case AUDIO: return "grabberAudio";
|
case AUDIO: return "grabberAudio";
|
||||||
case JSONSERVER: return "jsonServer";
|
case JSONSERVER: return "jsonServer";
|
||||||
case LEDCONFIG: return "ledConfig";
|
case LEDCONFIG: return "ledConfig";
|
||||||
case LEDS: return "leds";
|
case LEDS: return "leds";
|
||||||
case LOGGER: return "logger";
|
case LOGGER: return "logger";
|
||||||
case SMOOTHING: return "smoothing";
|
case SMOOTHING: return "smoothing";
|
||||||
case WEBSERVER: return "webConfig";
|
case WEBSERVER: return "webConfig";
|
||||||
case INSTCAPTURE: return "instCapture";
|
case INSTCAPTURE: return "instCapture";
|
||||||
case NETWORK: return "network";
|
case NETWORK: return "network";
|
||||||
case FLATBUFSERVER: return "flatbufServer";
|
case FLATBUFSERVER: return "flatbufServer";
|
||||||
case PROTOSERVER: return "protoServer";
|
case PROTOSERVER: return "protoServer";
|
||||||
default: return "invalid";
|
case OSEVENTS: return "osEvents";
|
||||||
|
case CECEVENTS: return "cecEvents";
|
||||||
|
case SCHEDEVENTS: return "schedEvents";
|
||||||
|
default: return "invalid";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +103,9 @@ namespace settings {
|
|||||||
else if (type == "network") return NETWORK;
|
else if (type == "network") return NETWORK;
|
||||||
else if (type == "flatbufServer") return FLATBUFSERVER;
|
else if (type == "flatbufServer") return FLATBUFSERVER;
|
||||||
else if (type == "protoServer") return PROTOSERVER;
|
else if (type == "protoServer") return PROTOSERVER;
|
||||||
|
else if (type == "osEvents") return OSEVENTS;
|
||||||
|
else if (type == "cecEvents") return CECEVENTS;
|
||||||
|
else if (type == "schedEvents") return SCHEDEVENTS;
|
||||||
else return INVALID;
|
else return INVALID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,12 @@ add_subdirectory(db)
|
|||||||
add_subdirectory(api)
|
add_subdirectory(api)
|
||||||
add_subdirectory(ssdp)
|
add_subdirectory(ssdp)
|
||||||
|
|
||||||
|
if(ENABLE_CEC)
|
||||||
|
add_subdirectory(cec)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(events)
|
||||||
|
|
||||||
if(ENABLE_MDNS)
|
if(ENABLE_MDNS)
|
||||||
add_subdirectory(mdns)
|
add_subdirectory(mdns)
|
||||||
endif()
|
endif()
|
||||||
@ -41,10 +47,6 @@ if(ENABLE_EFFECTENGINE)
|
|||||||
add_subdirectory(python)
|
add_subdirectory(python)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_CEC)
|
|
||||||
add_subdirectory(cec)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_EXPERIMENTAL)
|
if(ENABLE_EXPERIMENTAL)
|
||||||
add_subdirectory(experimental)
|
add_subdirectory(experimental)
|
||||||
endif()
|
endif()
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <grabber/qt/QtGrabber.h>
|
#include <grabber/qt/QtGrabber.h>
|
||||||
|
|
||||||
#include <utils/WeakConnect.h>
|
#include <utils/WeakConnect.h>
|
||||||
|
#include <events/EventEnum.h>
|
||||||
|
|
||||||
#if defined(ENABLE_MF)
|
#if defined(ENABLE_MF)
|
||||||
#include <grabber/video/mediafoundation/MFGrabber.h>
|
#include <grabber/video/mediafoundation/MFGrabber.h>
|
||||||
@ -82,6 +83,7 @@
|
|||||||
|
|
||||||
// api includes
|
// api includes
|
||||||
#include <api/JsonCB.h>
|
#include <api/JsonCB.h>
|
||||||
|
#include <events/EventHandler.h>
|
||||||
|
|
||||||
// auth manager
|
// auth manager
|
||||||
#include <hyperion/AuthManager.h>
|
#include <hyperion/AuthManager.h>
|
||||||
@ -110,6 +112,8 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject
|
|||||||
_ledStreamTimer = new QTimer(this);
|
_ledStreamTimer = new QTimer(this);
|
||||||
|
|
||||||
Q_INIT_RESOURCE(JSONRPC_schemas);
|
Q_INIT_RESOURCE(JSONRPC_schemas);
|
||||||
|
|
||||||
|
qRegisterMetaType<Event>("Event");
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonAPI::initialize()
|
void JsonAPI::initialize()
|
||||||
@ -135,16 +139,13 @@ void JsonAPI::initialize()
|
|||||||
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
//notify instance manager on suspend/resume/idle requests
|
//notify eventhadler on suspend/resume/idle requests
|
||||||
connect(this, &JsonAPI::suspendAll, _instanceManager, &HyperionIManager::triggerSuspend);
|
connect(this, &JsonAPI::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
connect(this, &JsonAPI::toggleSuspendAll, _instanceManager, &HyperionIManager::triggerToggleSuspend);
|
|
||||||
connect(this, &JsonAPI::idleAll, _instanceManager, &HyperionIManager::triggerIdle);
|
|
||||||
connect(this, &JsonAPI::toggleIdleAll, _instanceManager, &HyperionIManager::triggerToggleIdle);
|
|
||||||
|
|
||||||
connect(_ledStreamTimer, &QTimer::timeout, this, &JsonAPI::streamLedColorsUpdate, Qt::UniqueConnection);
|
connect(_ledStreamTimer, &QTimer::timeout, this, &JsonAPI::streamLedColorsUpdate, Qt::UniqueConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsonAPI::handleInstanceSwitch(quint8 inst, bool forced)
|
bool JsonAPI::handleInstanceSwitch(quint8 inst, bool /*forced*/)
|
||||||
{
|
{
|
||||||
if (API::setHyperionInstance(inst))
|
if (API::setHyperionInstance(inst))
|
||||||
{
|
{
|
||||||
@ -1014,8 +1015,7 @@ void JsonAPI::handleConfigCommand(const QJsonObject &message, const QString &com
|
|||||||
if (_adminAuthorized)
|
if (_adminAuthorized)
|
||||||
{
|
{
|
||||||
Debug(_log, "Restarting due to RPC command");
|
Debug(_log, "Restarting due to RPC command");
|
||||||
|
emit signalEvent(Event::Reload);
|
||||||
Process::restartHyperion(10);
|
|
||||||
|
|
||||||
sendSuccessReply(command + "-" + subcommand, tan);
|
sendSuccessReply(command + "-" + subcommand, tan);
|
||||||
}
|
}
|
||||||
@ -1852,32 +1852,37 @@ void JsonAPI::handleSystemCommand(const QJsonObject &message, const QString &com
|
|||||||
|
|
||||||
if (subc == "suspend")
|
if (subc == "suspend")
|
||||||
{
|
{
|
||||||
emit suspendAll(true);
|
emit signalEvent(Event::Suspend);
|
||||||
sendSuccessReply(command + "-" + subc, tan);
|
sendSuccessReply(command + "-" + subc, tan);
|
||||||
}
|
}
|
||||||
else if (subc == "resume")
|
else if (subc == "resume")
|
||||||
{
|
{
|
||||||
emit suspendAll(false);
|
emit signalEvent(Event::Resume);
|
||||||
sendSuccessReply(command + "-" + subc, tan);
|
sendSuccessReply(command + "-" + subc, tan);
|
||||||
}
|
}
|
||||||
else if (subc == "restart")
|
else if (subc == "restart")
|
||||||
{
|
{
|
||||||
Process::restartHyperion(11);
|
emit signalEvent(Event::Restart);
|
||||||
sendSuccessReply(command + "-" + subc, tan);
|
sendSuccessReply(command + "-" + subc, tan);
|
||||||
}
|
}
|
||||||
else if (subc == "toggleSuspend")
|
else if (subc == "toggleSuspend")
|
||||||
{
|
{
|
||||||
emit toggleSuspendAll();
|
emit signalEvent(Event::ToggleSuspend);
|
||||||
sendSuccessReply(command + "-" + subc, tan);
|
sendSuccessReply(command + "-" + subc, tan);
|
||||||
}
|
}
|
||||||
else if (subc == "idle")
|
else if (subc == "idle")
|
||||||
{
|
{
|
||||||
emit idleAll(true);
|
emit signalEvent(Event::Idle);
|
||||||
|
sendSuccessReply(command + "-" + subc, tan);
|
||||||
|
}
|
||||||
|
else if (subc == "resumeIdle")
|
||||||
|
{
|
||||||
|
emit signalEvent(Event::ResumeIdle);
|
||||||
sendSuccessReply(command + "-" + subc, tan);
|
sendSuccessReply(command + "-" + subc, tan);
|
||||||
}
|
}
|
||||||
else if (subc == "toggleIdle")
|
else if (subc == "toggleIdle")
|
||||||
{
|
{
|
||||||
emit toggleIdleAll();
|
emit signalEvent(Event::ToggleIdle);
|
||||||
sendSuccessReply(command + "-" + subc, tan);
|
sendSuccessReply(command + "-" + subc, tan);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <libcec/cecloader.h>
|
#include <libcec/cecloader.h>
|
||||||
|
#include <events/EventHandler.h>
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
@ -12,13 +13,21 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
/* Enable to turn on detailed CEC logs */
|
/* Enable to turn on detailed CEC logs */
|
||||||
#define NO_VERBOSE_CEC
|
#define NOVERBOSE_CEC
|
||||||
|
|
||||||
CECHandler::CECHandler()
|
CECHandler::CECHandler(const QJsonDocument& config, QObject * parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, _config(config)
|
||||||
|
, _isInitialised(false)
|
||||||
|
, _isEnabled(false)
|
||||||
|
, _buttonReleaseDelayMs(CEC_BUTTON_TIMEOUT)
|
||||||
|
, _buttonRepeatRateMs(0)
|
||||||
|
, _doubleTapTimeoutMs(CEC_DOUBLE_TAP_TIMEOUT_MS)
|
||||||
|
, _cecEventActionMap()
|
||||||
{
|
{
|
||||||
qRegisterMetaType<CECEvent>("CECEvent");
|
qRegisterMetaType<Event>("Event");
|
||||||
|
|
||||||
_logger = Logger::getInstance("CEC");
|
_logger = Logger::getInstance("EVENTS-CEC");
|
||||||
|
|
||||||
_cecCallbacks = getCallbacks();
|
_cecCallbacks = getCallbacks();
|
||||||
_cecConfig = getConfig();
|
_cecConfig = getConfig();
|
||||||
@ -28,66 +37,151 @@ CECHandler::CECHandler()
|
|||||||
|
|
||||||
CECHandler::~CECHandler()
|
CECHandler::~CECHandler()
|
||||||
{
|
{
|
||||||
stop();
|
}
|
||||||
|
|
||||||
|
void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||||
|
{
|
||||||
|
if(type == settings::CECEVENTS)
|
||||||
|
{
|
||||||
|
if (_isInitialised)
|
||||||
|
{
|
||||||
|
const QJsonObject& obj = config.object();
|
||||||
|
|
||||||
|
_isEnabled = obj["enable"].toBool(false);
|
||||||
|
Debug(_logger, "CEC Event handling is %s", _isEnabled? "enabled" : "disabled");
|
||||||
|
|
||||||
|
if (_isEnabled)
|
||||||
|
{
|
||||||
|
_buttonReleaseDelayMs = obj["buttonReleaseDelayMs"].toInt(CEC_BUTTON_TIMEOUT);
|
||||||
|
_buttonRepeatRateMs = obj["buttonRepeatRateMs"].toInt(0);
|
||||||
|
_doubleTapTimeoutMs = obj["doubleTapTimeoutMs"].toInt(CEC_DOUBLE_TAP_TIMEOUT_MS);
|
||||||
|
|
||||||
|
Debug(_logger, "Remote button press release time : %dms",_buttonReleaseDelayMs);
|
||||||
|
Debug(_logger, "Remote button press repeat rate : %dms",_buttonRepeatRateMs);
|
||||||
|
Debug(_logger, "Remote button press delay before repeating : %dms",_doubleTapTimeoutMs);
|
||||||
|
|
||||||
|
_cecConfig.iButtonReleaseDelayMs = static_cast<uint32_t>(_buttonReleaseDelayMs);
|
||||||
|
_cecConfig.iButtonRepeatRateMs = static_cast<uint32_t>(_buttonRepeatRateMs);
|
||||||
|
_cecConfig.iDoubleTapTimeoutMs = static_cast<uint32_t>(_doubleTapTimeoutMs);
|
||||||
|
|
||||||
|
_cecEventActionMap.clear();
|
||||||
|
const QJsonArray actionItems = obj["actions"].toArray();
|
||||||
|
if (!actionItems.isEmpty())
|
||||||
|
{
|
||||||
|
for (const QJsonValue &item : actionItems)
|
||||||
|
{
|
||||||
|
QString cecEvent = item.toObject().value("event").toString();
|
||||||
|
QString action = item.toObject().value("action").toString();
|
||||||
|
_cecEventActionMap.insert(cecEvent, stringToEvent(action));
|
||||||
|
Debug(_logger, "CEC-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(cecEvent), QSTRING_CSTR(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_cecEventActionMap.isEmpty())
|
||||||
|
{
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Warning(_logger, "No CEC events to listen to are configured currently.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CECHandler::start()
|
bool CECHandler::start()
|
||||||
{
|
{
|
||||||
if (_cecAdapter)
|
_isInitialised = false;
|
||||||
return true;
|
if (_cecAdapter == nullptr)
|
||||||
|
|
||||||
std::string library = std::string("" CEC_LIBRARY);
|
|
||||||
_cecAdapter = LibCecInitialise(&_cecConfig, QFile::exists(QString::fromStdString(library)) ? library.c_str() : nullptr);
|
|
||||||
if(!_cecAdapter)
|
|
||||||
{
|
{
|
||||||
Error(_logger, "Failed to loading libcec.so");
|
_cecAdapter = LibCecInitialise(&_cecConfig);
|
||||||
return false;
|
if(_cecAdapter == nullptr)
|
||||||
}
|
|
||||||
|
|
||||||
Info(_logger, "CEC handler started");
|
|
||||||
|
|
||||||
auto adapters = getAdapters();
|
|
||||||
if (adapters.isEmpty())
|
|
||||||
{
|
|
||||||
Error(_logger, "Failed to find CEC adapter");
|
|
||||||
UnloadLibCec(_cecAdapter);
|
|
||||||
_cecAdapter = nullptr;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Info(_logger, "Auto detecting CEC adapter");
|
|
||||||
bool opened = false;
|
|
||||||
for (const auto & adapter : adapters)
|
|
||||||
{
|
|
||||||
printAdapter(adapter);
|
|
||||||
|
|
||||||
if (!opened && openAdapter(adapter))
|
|
||||||
{
|
{
|
||||||
Info(_logger, "CEC Handler initialized with adapter : %s", adapter.strComName);
|
Error(_logger, "Failed loading libCEC library. CEC is not supported.");
|
||||||
|
|
||||||
opened = true;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_isInitialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSettingsUpdate(settings::CECEVENTS,_config);
|
||||||
|
}
|
||||||
|
return _isInitialised;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CECHandler::stop()
|
||||||
|
{
|
||||||
|
if (_cecAdapter != nullptr)
|
||||||
|
{
|
||||||
|
Info(_logger, "Stopping CEC handler");
|
||||||
|
_cecAdapter->Close();
|
||||||
|
UnloadLibCec(_cecAdapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CECHandler::enable()
|
||||||
|
{
|
||||||
|
bool opened {false};
|
||||||
|
if (_isInitialised)
|
||||||
|
{
|
||||||
|
const auto adapters = getAdapters();
|
||||||
|
if (adapters.isEmpty())
|
||||||
|
{
|
||||||
|
Error(_logger, "Failed to find any CEC adapter. CEC event handling will be disabled.");
|
||||||
|
_cecAdapter->Close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Info(_logger, "Auto detecting CEC adapter");
|
||||||
|
for (const auto & adapter : adapters)
|
||||||
|
{
|
||||||
|
printAdapter(adapter);
|
||||||
|
|
||||||
|
if (!opened && openAdapter(adapter))
|
||||||
|
{
|
||||||
|
Info(_logger, "CEC adapter '%s', type: %s initialized." , adapter.strComName, _cecAdapter->ToString(adapter.adapterType));
|
||||||
|
opened = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VERBOSE_CEC
|
||||||
|
std::cout << "Found Devices: " << scan().toStdString() << std::endl;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opened)
|
if (!opened)
|
||||||
{
|
{
|
||||||
UnloadLibCec(_cecAdapter);
|
Error(_logger, "Could not initialize any CEC adapter.");
|
||||||
_cecAdapter = nullptr;
|
_cecAdapter->Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_cecAdapter->SetConfiguration(&_cecConfig))
|
||||||
|
{
|
||||||
|
Error(_logger, "Failed setting remote button press timing parameters");
|
||||||
|
}
|
||||||
|
QObject::connect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
|
Info(_logger, "CEC handler started");
|
||||||
}
|
}
|
||||||
|
|
||||||
return opened;
|
return opened;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CECHandler::stop()
|
void CECHandler::disable()
|
||||||
{
|
{
|
||||||
if (_cecAdapter)
|
if (_isInitialised)
|
||||||
{
|
{
|
||||||
Info(_logger, "Stopping CEC handler");
|
Info(_logger, "Stopping CEC handler");
|
||||||
|
|
||||||
|
QObject::disconnect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
|
|
||||||
_cecAdapter->Close();
|
_cecAdapter->Close();
|
||||||
UnloadLibCec(_cecAdapter);
|
|
||||||
_cecAdapter = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +191,6 @@ CECConfig CECHandler::getConfig() const
|
|||||||
|
|
||||||
const std::string name("HyperionCEC");
|
const std::string name("HyperionCEC");
|
||||||
name.copy(configuration.strDeviceName, std::min(name.size(), sizeof(configuration.strDeviceName)));
|
name.copy(configuration.strDeviceName, std::min(name.size(), sizeof(configuration.strDeviceName)));
|
||||||
|
|
||||||
configuration.deviceTypes.Add(CEC::CEC_DEVICE_TYPE_RECORDING_DEVICE);
|
configuration.deviceTypes.Add(CEC::CEC_DEVICE_TYPE_RECORDING_DEVICE);
|
||||||
configuration.clientVersion = CEC::LIBCEC_VERSION_CURRENT;
|
configuration.clientVersion = CEC::LIBCEC_VERSION_CURRENT;
|
||||||
|
|
||||||
@ -121,11 +214,11 @@ CECCallbacks CECHandler::getCallbacks() const
|
|||||||
|
|
||||||
QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
|
QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
|
||||||
{
|
{
|
||||||
if (!_cecAdapter)
|
if (_cecAdapter == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
QVector<CECAdapterDescriptor> descriptors(16);
|
QVector<CECAdapterDescriptor> descriptors(16);
|
||||||
int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), descriptors.size(), nullptr, true /*quickscan*/);
|
int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), static_cast<uint8_t>(descriptors.size()), nullptr, true /*quickscan*/);
|
||||||
descriptors.resize(size);
|
descriptors.resize(size);
|
||||||
|
|
||||||
return descriptors;
|
return descriptors;
|
||||||
@ -133,15 +226,12 @@ QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
|
|||||||
|
|
||||||
bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
||||||
{
|
{
|
||||||
if (!_cecAdapter)
|
if (_cecAdapter == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!_cecAdapter->Open(descriptor.strComName))
|
if(!_cecAdapter->Open(descriptor.strComName))
|
||||||
{
|
{
|
||||||
Error(_logger, "%s", QSTRING_CSTR(QString("Failed to open the CEC adaper on port %1")
|
Error(_logger, "CEC adapter '%s', type: %s failed to open.", descriptor.strComName, _cecAdapter->ToString(descriptor.adapterType));
|
||||||
.arg(descriptor.strComName))
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -149,28 +239,44 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
|||||||
|
|
||||||
void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const
|
void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const
|
||||||
{
|
{
|
||||||
Info(_logger, "%s", QSTRING_CSTR(QString("CEC Adapter:")));
|
Debug(_logger, "CEC Adapter:");
|
||||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tName : %1").arg(descriptor.strComName)));
|
Debug(_logger, "\tName : %s", descriptor.strComName);
|
||||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tPath : %1").arg(descriptor.strComPath)));
|
Debug(_logger, "\tPath : %s", descriptor.strComPath);
|
||||||
|
if (descriptor.iVendorId != 0)
|
||||||
|
{
|
||||||
|
Debug(_logger, "\tVendor id: %04x", descriptor.iVendorId);
|
||||||
|
}
|
||||||
|
if (descriptor.iProductId != 0)
|
||||||
|
{
|
||||||
|
Debug(_logger, "\tProduct id: %04x", descriptor.iProductId);
|
||||||
|
}
|
||||||
|
if (descriptor.iFirmwareVersion != 0)
|
||||||
|
{
|
||||||
|
Debug(_logger, "\tFirmware id: %d", descriptor.iFirmwareVersion);
|
||||||
|
}
|
||||||
|
if (descriptor.adapterType != CEC::ADAPTERTYPE_UNKNOWN)
|
||||||
|
{
|
||||||
|
Debug(_logger, "\tType : %s", _cecAdapter->ToString(descriptor.adapterType));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CECHandler::scan() const
|
QString CECHandler::scan() const
|
||||||
{
|
{
|
||||||
if (!_cecAdapter)
|
if (_cecAdapter == nullptr)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
Info(_logger, "Starting CEC scan");
|
Info(_logger, "Starting CEC scan");
|
||||||
|
|
||||||
QJsonArray devices;
|
QJsonArray devices;
|
||||||
CECLogicalAddresses addresses = _cecAdapter->GetActiveDevices();
|
CECLogicalAddresses addresses = _cecAdapter->GetActiveDevices();
|
||||||
for (int address = CEC::CECDEVICE_TV; address <= CEC::CECDEVICE_BROADCAST; ++address)
|
for (uint8_t address = CEC::CECDEVICE_TV; address <= CEC::CECDEVICE_BROADCAST; ++address)
|
||||||
{
|
{
|
||||||
if (addresses[address])
|
if (addresses[address] != 0)
|
||||||
{
|
{
|
||||||
CECLogicalAddress logicalAddress = (CECLogicalAddress)address;
|
CECLogicalAddress logicalAddress = static_cast<CECLogicalAddress>(address);
|
||||||
|
|
||||||
QJsonObject device;
|
QJsonObject device;
|
||||||
CECVendorId vendor = (CECVendorId)_cecAdapter->GetDeviceVendorId(logicalAddress);
|
CECVendorId vendor = static_cast<CECVendorId>(_cecAdapter->GetDeviceVendorId(logicalAddress));
|
||||||
CECPowerStatus power = _cecAdapter->GetDevicePowerStatus(logicalAddress);
|
CECPowerStatus power = _cecAdapter->GetDevicePowerStatus(logicalAddress);
|
||||||
|
|
||||||
device["name" ] = _cecAdapter->GetDeviceOSDName(logicalAddress).c_str();
|
device["name" ] = _cecAdapter->GetDeviceOSDName(logicalAddress).c_str();
|
||||||
@ -181,46 +287,48 @@ QString CECHandler::scan() const
|
|||||||
devices << device;
|
devices << device;
|
||||||
|
|
||||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tCECDevice: %1 / %2 / %3 / %4")
|
Info(_logger, "%s", QSTRING_CSTR(QString("\tCECDevice: %1 / %2 / %3 / %4")
|
||||||
.arg(device["name"].toString(),
|
.arg(device["name"].toString(),
|
||||||
device["vendor"].toString(),
|
device["vendor"].toString(),
|
||||||
device["address"].toString(),
|
device["address"].toString(),
|
||||||
device["power"].toString()))
|
device["power"].toString())
|
||||||
);
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QJsonDocument(devices).toJson(QJsonDocument::Compact);
|
return QJsonDocument(devices).toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CECHandler::triggerAction(const QString& cecEvent)
|
||||||
|
{
|
||||||
|
Event action = _cecEventActionMap.value(cecEvent, Event::Unknown);
|
||||||
|
Debug(_logger, "CEC-Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(cecEvent), eventToString(action) );
|
||||||
|
if ( action != Event::Unknown )
|
||||||
|
{
|
||||||
|
emit signalEvent(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message)
|
void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message)
|
||||||
{
|
{
|
||||||
#ifdef VERBOSE_CEC
|
#ifdef VERBOSE_CEC
|
||||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||||
if (!handler)
|
if (handler == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (message->level)
|
switch (message->level)
|
||||||
{
|
{
|
||||||
case CEC::CEC_LOG_ERROR:
|
case CEC::CEC_LOG_ERROR:
|
||||||
Error(handler->_logger, QString("%1")
|
Error(handler->_logger, "%s", message->message);
|
||||||
.arg(message->message)
|
|
||||||
.toLocal8Bit());
|
|
||||||
break;
|
break;
|
||||||
case CEC::CEC_LOG_WARNING:
|
case CEC::CEC_LOG_WARNING:
|
||||||
Warning(handler->_logger, QString("%1")
|
Warning(handler->_logger, "%s", message->message);
|
||||||
.arg(message->message)
|
|
||||||
.toLocal8Bit());
|
|
||||||
break;
|
break;
|
||||||
case CEC::CEC_LOG_TRAFFIC:
|
case CEC::CEC_LOG_TRAFFIC:
|
||||||
case CEC::CEC_LOG_NOTICE:
|
case CEC::CEC_LOG_NOTICE:
|
||||||
Info(handler->_logger, QString("%1")
|
Info(handler->_logger, "%s", message->message);
|
||||||
.arg(message->message)
|
|
||||||
.toLocal8Bit());
|
|
||||||
break;
|
break;
|
||||||
case CEC::CEC_LOG_DEBUG:
|
case CEC::CEC_LOG_DEBUG:
|
||||||
Debug(handler->_logger, QString("%1")
|
Debug(handler->_logger, "%s", message->message);
|
||||||
.arg(message->message)
|
|
||||||
.toLocal8Bit());
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -230,29 +338,36 @@ void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message)
|
|||||||
|
|
||||||
void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key)
|
void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key)
|
||||||
{
|
{
|
||||||
#ifdef VERBOSE_CEC
|
|
||||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||||
if (!handler)
|
if (handler == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CECAdapter * adapter = handler->_cecAdapter;
|
CECAdapter * adapter = handler->_cecAdapter;
|
||||||
|
|
||||||
Debug(handler->_logger, QString("CECHandler::onCecKeyPress: %1")
|
#ifdef VERBOSE_CEC
|
||||||
.arg(adapter->ToString(key->keycode))
|
Debug(handler->_logger, "CECHandler::onCecKeyPress: %s", adapter->ToString(key->keycode));
|
||||||
.toLocal8Bit());
|
|
||||||
#endif
|
#endif
|
||||||
|
switch (key->keycode) {
|
||||||
|
case CEC::CEC_USER_CONTROL_CODE_F1_BLUE:
|
||||||
|
case CEC::CEC_USER_CONTROL_CODE_F2_RED:
|
||||||
|
case CEC::CEC_USER_CONTROL_CODE_F3_GREEN:
|
||||||
|
case CEC::CEC_USER_CONTROL_CODE_F4_YELLOW:
|
||||||
|
handler->triggerAction(adapter->ToString(key->keycode));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParameter data)
|
void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParameter /* data */)
|
||||||
{
|
{
|
||||||
#ifdef VERBOSE_CEC
|
#ifdef VERBOSE_CEC
|
||||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||||
if (!handler)
|
if (handler == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Error(handler->_logger, QString("CECHandler::onCecAlert: %1")
|
Error(handler->_logger, QSTRING_CSTR(QString("CECHandler::onCecAlert: %1")
|
||||||
.arg(alert)
|
.arg(alert)));
|
||||||
.toLocal8Bit());
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,12 +375,10 @@ void CECHandler::onCecConfigurationChanged(void * context, const CECConfig * con
|
|||||||
{
|
{
|
||||||
#ifdef VERBOSE_CEC
|
#ifdef VERBOSE_CEC
|
||||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||||
if (!handler)
|
if (handler == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Debug(handler->_logger, QString("CECHandler::onCecConfigurationChanged: %1")
|
Debug(handler->_logger, "CECHandler::onCecConfigurationChanged: %s", configuration->strDeviceName);
|
||||||
.arg(configuration->strDeviceName)
|
|
||||||
.toLocal8Bit());
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,9 +391,7 @@ int CECHandler::onCecMenuStateChanged(void * context, const CECMenuState state)
|
|||||||
|
|
||||||
CECAdapter * adapter = handler->_cecAdapter;
|
CECAdapter * adapter = handler->_cecAdapter;
|
||||||
|
|
||||||
Debug(handler->_logger, QString("CECHandler::onCecMenuStateChanged: %1")
|
Debug(handler->_logger, "CECHandler::onCecMenuStateChanged: %s", adapter->ToString(state));
|
||||||
.arg(adapter->ToString(state))
|
|
||||||
.toLocal8Bit());
|
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -294,28 +405,25 @@ void CECHandler::onCecCommandReceived(void * context, const CECCommand * command
|
|||||||
CECAdapter * adapter = handler->_cecAdapter;
|
CECAdapter * adapter = handler->_cecAdapter;
|
||||||
|
|
||||||
#ifdef VERBOSE_CEC
|
#ifdef VERBOSE_CEC
|
||||||
Debug(handler->_logger, QString("CECHandler::onCecCommandReceived: %1 (%2 > %3)")
|
Debug(handler->_logger, "CECHandler::onCecCommandReceived: %s %s > %s)",
|
||||||
.arg(adapter->ToString(command->opcode))
|
adapter->ToString(command->opcode),
|
||||||
.arg(adapter->ToString(command->initiator))
|
adapter->ToString(command->initiator),
|
||||||
.arg(adapter->ToString(command->destination))
|
adapter->ToString(command->destination)
|
||||||
.toLocal8Bit());
|
);
|
||||||
#endif
|
#endif
|
||||||
/* We do NOT check sender */
|
/* We do NOT check sender */
|
||||||
// if (address == CEC::CECDEVICE_TV)
|
//if (address == CEC::CECDEVICE_TV)
|
||||||
{
|
{
|
||||||
if (command->opcode == CEC::CEC_OPCODE_SET_STREAM_PATH)
|
switch (command->opcode) {
|
||||||
|
case CEC::CEC_OPCODE_STANDBY:
|
||||||
|
case CEC::CEC_OPCODE_SET_STREAM_PATH:
|
||||||
{
|
{
|
||||||
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source activated: %1")
|
handler->triggerAction(adapter->ToString(command->opcode));
|
||||||
.arg(adapter->ToString(command->initiator)))
|
|
||||||
);
|
|
||||||
emit handler->cecEvent(CECEvent::On);
|
|
||||||
}
|
}
|
||||||
if (command->opcode == CEC::CEC_OPCODE_STANDBY)
|
break;
|
||||||
{
|
|
||||||
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source deactivated: %1")
|
default:
|
||||||
.arg(adapter->ToString(command->initiator)))
|
break;
|
||||||
);
|
|
||||||
emit handler->cecEvent(CECEvent::Off);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,15 +435,14 @@ void CECHandler::onCecSourceActivated(void * context, const CECLogicalAddress ad
|
|||||||
|
|
||||||
#ifdef VERBOSE_CEC
|
#ifdef VERBOSE_CEC
|
||||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||||
if (!handler)
|
if (handler == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CECAdapter * adapter = handler->_cecAdapter;
|
CECAdapter * adapter = handler->_cecAdapter;
|
||||||
|
Debug(handler->_logger, QSTRING_CSTR(QString("CEC source %1 : %2")
|
||||||
Debug(handler->_logger, QString("CEC source %1 : %2")
|
.arg(activated ? "activated" : "deactivated",
|
||||||
.arg(activated ? "activated" : "deactivated")
|
adapter->ToString(address))
|
||||||
.arg(adapter->ToString(address))
|
));
|
||||||
.toLocal8Bit());
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(cechandler
|
add_library(cechandler
|
||||||
${CMAKE_SOURCE_DIR}/include/cec/CECEvent.h
|
|
||||||
${CMAKE_SOURCE_DIR}/include/cec/CECHandler.h
|
${CMAKE_SOURCE_DIR}/include/cec/CECHandler.h
|
||||||
${CMAKE_SOURCE_DIR}/libsrc/cec/CECHandler.cpp
|
${CMAKE_SOURCE_DIR}/libsrc/cec/CECHandler.cpp
|
||||||
)
|
)
|
||||||
|
27
libsrc/events/CMakeLists.txt
Normal file
27
libsrc/events/CMakeLists.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
add_library(events
|
||||||
|
${CMAKE_SOURCE_DIR}/include/events/EventEnum.h
|
||||||
|
${CMAKE_SOURCE_DIR}/include/events/EventHandler.h
|
||||||
|
${CMAKE_SOURCE_DIR}/include/events/OsEventHandler.h
|
||||||
|
${CMAKE_SOURCE_DIR}/include/events/EventScheduler.h
|
||||||
|
${CMAKE_SOURCE_DIR}/libsrc/events/EventHandler.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/libsrc/events/OsEventHandler.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/libsrc/events/EventScheduler.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus QUIET)
|
||||||
|
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
||||||
|
target_link_libraries(events Qt${QT_VERSION_MAJOR}::DBus)
|
||||||
|
target_compile_definitions(events PRIVATE HYPERION_HAS_DBUS)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(events
|
||||||
|
hyperion-utils
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set_source_files_properties(OsEventHandler.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
|
||||||
|
target_link_libraries(events "-framework AppKit")
|
||||||
|
endif()
|
186
libsrc/events/EventHandler.cpp
Normal file
186
libsrc/events/EventHandler.cpp
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#include <events/EventHandler.h>
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include <utils/Logger.h>
|
||||||
|
#include <utils/Process.h>
|
||||||
|
#include <hyperion/HyperionIManager.h>
|
||||||
|
|
||||||
|
EventHandler::EventHandler()
|
||||||
|
: _isSuspended(false)
|
||||||
|
, _isIdle(false)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Event>("Event");
|
||||||
|
_log = Logger::getInstance("EVENTS");
|
||||||
|
|
||||||
|
QObject::connect(this, &EventHandler::signalEvent, HyperionIManager::getInstance(), &HyperionIManager::handleEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandler::~EventHandler()
|
||||||
|
{
|
||||||
|
QObject::disconnect(this, &EventHandler::signalEvent, HyperionIManager::getInstance(), &HyperionIManager::handleEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventHandler* EventHandler::getInstance()
|
||||||
|
{
|
||||||
|
static EventHandler instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::suspend()
|
||||||
|
{
|
||||||
|
suspend(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::suspend(bool sleep)
|
||||||
|
{
|
||||||
|
if (sleep)
|
||||||
|
{
|
||||||
|
if (!_isSuspended)
|
||||||
|
{
|
||||||
|
_isSuspended = true;
|
||||||
|
Info(_log, "Suspend event received - Hyperion is going to sleep");
|
||||||
|
emit signalEvent(Event::Suspend);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug(_log, "Suspend event ignored - already suspended");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_isSuspended || _isIdle)
|
||||||
|
{
|
||||||
|
Info(_log, "Resume event received - Hyperion is going into working mode");
|
||||||
|
emit signalEvent(Event::Resume);
|
||||||
|
_isSuspended = false;
|
||||||
|
_isIdle = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug(_log, "Resume event ignored - not in suspend nor idle mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::resume()
|
||||||
|
{
|
||||||
|
suspend(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::toggleSuspend()
|
||||||
|
{
|
||||||
|
Debug(_log, "Toggle suspend event received");
|
||||||
|
if (!_isSuspended)
|
||||||
|
{
|
||||||
|
suspend(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
suspend(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::idle()
|
||||||
|
{
|
||||||
|
idle(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::idle(bool isIdle)
|
||||||
|
{
|
||||||
|
if (!_isSuspended)
|
||||||
|
{
|
||||||
|
if (isIdle)
|
||||||
|
{
|
||||||
|
if (!_isIdle)
|
||||||
|
{
|
||||||
|
_isIdle = true;
|
||||||
|
Info(_log, "Idle event received");
|
||||||
|
emit signalEvent(Event::Idle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_isIdle)
|
||||||
|
{
|
||||||
|
Info(_log, "Resume from idle event recevied");
|
||||||
|
emit signalEvent(Event::ResumeIdle);
|
||||||
|
_isIdle = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug(_log, "Idle event ignored - Hyperion is suspended");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void EventHandler::resumeIdle()
|
||||||
|
{
|
||||||
|
idle(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::toggleIdle()
|
||||||
|
{
|
||||||
|
Debug(_log, "Toggle idle event received");
|
||||||
|
if (!_isIdle)
|
||||||
|
{
|
||||||
|
idle(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
idle(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventHandler::handleEvent(Event event)
|
||||||
|
{
|
||||||
|
QObject *senderObj = QObject::sender();
|
||||||
|
QString senderObjectClass;
|
||||||
|
if (senderObj)
|
||||||
|
{
|
||||||
|
senderObjectClass = senderObj->metaObject()->className();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
senderObjectClass = "unknown sender";
|
||||||
|
}
|
||||||
|
Debug(_log,"%s Event [%d] received from %s", eventToString(event), event, QSTRING_CSTR(senderObjectClass));
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case Event::Suspend:
|
||||||
|
suspend();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::Resume:
|
||||||
|
resume();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::ToggleSuspend:
|
||||||
|
toggleSuspend();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::Idle:
|
||||||
|
idle(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::ResumeIdle:
|
||||||
|
idle(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::ToggleIdle:
|
||||||
|
toggleIdle();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::Reload:
|
||||||
|
Process::restartHyperion(10);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::Restart:
|
||||||
|
Process::restartHyperion(11);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Error(_log,"Unkonwn Event '%d' received", event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
153
libsrc/events/EventScheduler.cpp
Normal file
153
libsrc/events/EventScheduler.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include "events/EventScheduler.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include <events/EventHandler.h>
|
||||||
|
#include <utils/Logger.h>
|
||||||
|
|
||||||
|
EventScheduler::EventScheduler()
|
||||||
|
: _isEnabled(false)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Event>("Event");
|
||||||
|
_log = Logger::getInstance("EVENTS-SCHED");
|
||||||
|
|
||||||
|
QObject::connect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventScheduler::~EventScheduler()
|
||||||
|
{
|
||||||
|
QObject::disconnect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
|
clearTimers();
|
||||||
|
Info(_log, "Event scheduler stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventScheduler::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||||
|
{
|
||||||
|
if(type == settings::SCHEDEVENTS)
|
||||||
|
{
|
||||||
|
const QJsonObject& obj = config.object();
|
||||||
|
|
||||||
|
_isEnabled = obj["enable"].toBool(false);
|
||||||
|
Debug(_log, "Event scheduler is %s", _isEnabled? "enabled" : "disabled");
|
||||||
|
|
||||||
|
if (_isEnabled)
|
||||||
|
{
|
||||||
|
_scheduledEvents.clear();
|
||||||
|
|
||||||
|
const QJsonArray actionItems = obj["actions"].toArray();
|
||||||
|
if (!actionItems.isEmpty())
|
||||||
|
{
|
||||||
|
timeEvent timeEvent;
|
||||||
|
for (const QJsonValue &item : actionItems)
|
||||||
|
{
|
||||||
|
QString action = item.toObject().value("action").toString();
|
||||||
|
timeEvent.action = stringToEvent(action);
|
||||||
|
|
||||||
|
QString event = item.toObject().value("event").toString();
|
||||||
|
timeEvent.time = QTime::fromString(event,"hh:mm");
|
||||||
|
if (timeEvent.time.isValid())
|
||||||
|
{
|
||||||
|
_scheduledEvents.append(timeEvent);
|
||||||
|
Debug(_log, "Time-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Error in configured time : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_scheduledEvents.isEmpty())
|
||||||
|
{
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Warning(_log, "No scheduled events to listen to are configured currently.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventScheduler::enable()
|
||||||
|
{
|
||||||
|
bool enabled {false};
|
||||||
|
|
||||||
|
clearTimers();
|
||||||
|
for (int i = 0; i < _scheduledEvents.size(); ++i)
|
||||||
|
{
|
||||||
|
QTimer* timer = new QTimer(this);
|
||||||
|
timer->setTimerType(Qt::PreciseTimer);
|
||||||
|
_timers.append(timer);
|
||||||
|
|
||||||
|
// Calculate the milliseconds until the next occurrence of the scheduled time
|
||||||
|
int milliseconds = getMillisecondsToNextScheduledTime(_scheduledEvents.at(i).time);
|
||||||
|
timer->start(milliseconds);
|
||||||
|
|
||||||
|
QObject::connect(timer, &QTimer::timeout, this, [this, i]() { handleEvent(i); });
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
Info(_log, "Event scheduler started");
|
||||||
|
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventScheduler::disable()
|
||||||
|
{
|
||||||
|
Info(_log, "Disabling event scheduler");
|
||||||
|
clearTimers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventScheduler::clearTimers()
|
||||||
|
{
|
||||||
|
for (QTimer *timer : std::as_const(_timers)) {
|
||||||
|
timer->disconnect();
|
||||||
|
timer->stop();
|
||||||
|
delete timer;
|
||||||
|
}
|
||||||
|
_timers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventScheduler::handleEvent(int timerIndex)
|
||||||
|
{
|
||||||
|
QTime time = _scheduledEvents.at(timerIndex).time;
|
||||||
|
Event action = _scheduledEvents.at(timerIndex).action;
|
||||||
|
Debug(_log, "Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(time.toString()), eventToString(action) );
|
||||||
|
if ( action != Event::Unknown )
|
||||||
|
{
|
||||||
|
emit signalEvent(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrigger the timer for the next occurrence
|
||||||
|
QTimer* timer = _timers.at(timerIndex);
|
||||||
|
int milliseconds = getMillisecondsToNextScheduledTime(time);
|
||||||
|
timer->start(milliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
int EventScheduler::getMillisecondsToNextScheduledTime(const QTime& scheduledTime)
|
||||||
|
{
|
||||||
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
|
QTime currentTime = currentDateTime.time();
|
||||||
|
|
||||||
|
QDateTime nextOccurrence = currentDateTime;
|
||||||
|
nextOccurrence.setTime(scheduledTime);
|
||||||
|
|
||||||
|
// If the scheduled time has already passed for today, schedule it for tomorrow
|
||||||
|
if (currentTime > scheduledTime)
|
||||||
|
{
|
||||||
|
nextOccurrence = nextOccurrence.addDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int milliseconds = currentDateTime.msecsTo(nextOccurrence);
|
||||||
|
return (milliseconds > 0) ? milliseconds : 0;
|
||||||
|
}
|
626
libsrc/events/OsEventHandler.cpp
Normal file
626
libsrc/events/OsEventHandler.cpp
Normal file
@ -0,0 +1,626 @@
|
|||||||
|
#include "events/OsEventHandler.h"
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include <events/EventHandler.h>
|
||||||
|
#include <utils/Logger.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <wtsapi32.h>
|
||||||
|
|
||||||
|
#pragma comment( lib, "wtsapi32.lib" )
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <AppKit/AppKit.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OsEventHandlerBase::OsEventHandlerBase()
|
||||||
|
: _isSuspendEnabled(false)
|
||||||
|
, _isLockEnabled(false)
|
||||||
|
, _isSuspendOnLock(false)
|
||||||
|
, _isSuspendRegistered(false)
|
||||||
|
, _isLockRegistered(false)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Event>("Event");
|
||||||
|
_log = Logger::getInstance("EVENTS-OS");
|
||||||
|
|
||||||
|
QObject::connect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
OsEventHandlerBase::~OsEventHandlerBase()
|
||||||
|
{
|
||||||
|
QObject::disconnect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
|
|
||||||
|
OsEventHandlerBase::unregisterLockHandler();
|
||||||
|
OsEventHandlerBase::unregisterOsEventHandler();
|
||||||
|
|
||||||
|
Info(_log, "Operating System event handler stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerBase::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||||
|
{
|
||||||
|
if(type == settings::OSEVENTS)
|
||||||
|
{
|
||||||
|
const QJsonObject& obj = config.object();
|
||||||
|
|
||||||
|
//Suspend on lock or go into idle mode
|
||||||
|
bool prevIsSuspendOnLock = _isSuspendOnLock;
|
||||||
|
_isSuspendOnLock = obj["suspendOnLockEnable"].toBool(false);
|
||||||
|
|
||||||
|
//Handle OS event related configurations
|
||||||
|
_isSuspendEnabled = obj["suspendEnable"].toBool(true);
|
||||||
|
if (_isSuspendEnabled)
|
||||||
|
{
|
||||||
|
// Listen to suspend/resume/idle events received by the OS
|
||||||
|
registerOsEventHandler();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unregisterOsEventHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLockEnabled = obj["lockEnable"].toBool(true);
|
||||||
|
if (_isLockEnabled || _isSuspendOnLock != prevIsSuspendOnLock)
|
||||||
|
{
|
||||||
|
// Listen to lock/screensaver events received by the OS
|
||||||
|
registerLockHandler();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unregisterLockHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerBase::suspend(bool sleep)
|
||||||
|
{
|
||||||
|
if (sleep)
|
||||||
|
{
|
||||||
|
emit signalEvent(Event::Suspend);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit signalEvent(Event::Resume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerBase::lock(bool isLocked)
|
||||||
|
{
|
||||||
|
if (isLocked)
|
||||||
|
{
|
||||||
|
if (_isSuspendOnLock)
|
||||||
|
{
|
||||||
|
emit signalEvent(Event::Suspend);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit signalEvent(Event::Idle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_isSuspendOnLock)
|
||||||
|
{
|
||||||
|
emit signalEvent(Event::Resume);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit signalEvent(Event::ResumeIdle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
|
||||||
|
OsEventHandlerWindows::OsEventHandlerWindows()
|
||||||
|
: _notifyHandle(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OsEventHandlerWindows::~OsEventHandlerWindows()
|
||||||
|
{
|
||||||
|
unregisterLockHandler();
|
||||||
|
unregisterOsEventHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, qintptr* /*result*/)
|
||||||
|
#else
|
||||||
|
bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, long int* /*result*/)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
MSG* msg = static_cast<MSG*>(message);
|
||||||
|
|
||||||
|
switch (msg->message)
|
||||||
|
{
|
||||||
|
case WM_WTSSESSION_CHANGE:
|
||||||
|
switch (msg->wParam)
|
||||||
|
{
|
||||||
|
case WTS_SESSION_LOCK:
|
||||||
|
emit lock(true);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case WTS_SESSION_UNLOCK:
|
||||||
|
emit lock(false);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_POWERBROADCAST:
|
||||||
|
switch (msg->wParam)
|
||||||
|
{
|
||||||
|
case PBT_APMRESUMESUSPEND:
|
||||||
|
emit suspend(false);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case PBT_APMSUSPEND:
|
||||||
|
emit suspend(true);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OsEventHandlerWindows::registerOsEventHandler()
|
||||||
|
{
|
||||||
|
bool isRegistered{ _isSuspendRegistered };
|
||||||
|
if (!_isSuspendRegistered)
|
||||||
|
{
|
||||||
|
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
||||||
|
_notifyHandle = RegisterSuspendResumeNotification(handle, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||||
|
if (_notifyHandle != NULL)
|
||||||
|
{
|
||||||
|
QCoreApplication::instance()->installNativeEventFilter(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not register for suspend/resume events!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRegistered)
|
||||||
|
{
|
||||||
|
_isSuspendRegistered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerWindows::unregisterOsEventHandler()
|
||||||
|
{
|
||||||
|
if (_isSuspendRegistered)
|
||||||
|
{
|
||||||
|
if (_notifyHandle != NULL)
|
||||||
|
{
|
||||||
|
QCoreApplication::instance()->removeNativeEventFilter(this);
|
||||||
|
UnregisterSuspendResumeNotification(_notifyHandle);
|
||||||
|
}
|
||||||
|
_notifyHandle = NULL;
|
||||||
|
_isSuspendRegistered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OsEventHandlerWindows::registerLockHandler()
|
||||||
|
{
|
||||||
|
bool isRegistered{ _isLockRegistered };
|
||||||
|
if (!_isLockRegistered)
|
||||||
|
{
|
||||||
|
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
||||||
|
if (WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION))
|
||||||
|
{
|
||||||
|
isRegistered = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not register for lock/unlock events!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRegistered)
|
||||||
|
{
|
||||||
|
_isLockRegistered = true;
|
||||||
|
}
|
||||||
|
return isRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerWindows::unregisterLockHandler()
|
||||||
|
{
|
||||||
|
if (_isLockRegistered)
|
||||||
|
{
|
||||||
|
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
||||||
|
WTSUnRegisterSessionNotification(handle);
|
||||||
|
_isLockRegistered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__linux__)
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
|
OsEventHandlerLinux* OsEventHandlerLinux::getInstance()
|
||||||
|
{
|
||||||
|
static OsEventHandlerLinux instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
OsEventHandlerLinux::OsEventHandlerLinux()
|
||||||
|
{
|
||||||
|
signal(SIGUSR1, static_signaleHandler);
|
||||||
|
signal(SIGUSR2, static_signaleHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerLinux::handleSignal (int signum)
|
||||||
|
{
|
||||||
|
if (signum == SIGUSR1)
|
||||||
|
{
|
||||||
|
suspend(true);
|
||||||
|
}
|
||||||
|
else if (signum == SIGUSR2)
|
||||||
|
{
|
||||||
|
suspend(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HYPERION_HAS_DBUS)
|
||||||
|
#include <QDBusConnection>
|
||||||
|
|
||||||
|
struct dBusSignals
|
||||||
|
{
|
||||||
|
QString service;
|
||||||
|
QString path;
|
||||||
|
QString interface;
|
||||||
|
QString name;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef QMultiMap<QString, dBusSignals> DbusSignalsMap;
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
namespace {
|
||||||
|
const DbusSignalsMap dbusSignals = {
|
||||||
|
//system signals
|
||||||
|
{"Suspend", {"org.freedesktop.login1","/org/freedesktop/login1","org.freedesktop.login1.Manager","PrepareForSleep"}},
|
||||||
|
|
||||||
|
//Session signals
|
||||||
|
{"ScreenSaver", {"org.freedesktop.ScreenSaver","/org/freedesktop/ScreenSaver","org.freedesktop.ScreenSaver","ActiveChanged"}},
|
||||||
|
{"ScreenSaver", {"org.gnome.ScreenSaver","/org/gnome/ScreenSaver","org.gnome.ScreenSaver","ActiveChanged"}},
|
||||||
|
};
|
||||||
|
} //End of constants
|
||||||
|
|
||||||
|
bool OsEventHandlerLinux::registerOsEventHandler()
|
||||||
|
{
|
||||||
|
|
||||||
|
bool isRegistered {_isSuspendRegistered};
|
||||||
|
if (!_isSuspendRegistered)
|
||||||
|
{
|
||||||
|
QDBusConnection systemBus = QDBusConnection::systemBus();
|
||||||
|
if (!systemBus.isConnected())
|
||||||
|
{
|
||||||
|
Info(_log, "The suspend/resume feature is not supported by your system configuration");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString service = dbusSignals.find("Suspend").value().service;
|
||||||
|
if (systemBus.connect(service,
|
||||||
|
dbusSignals.find("Suspend").value().path,
|
||||||
|
dbusSignals.find("Suspend").value().interface,
|
||||||
|
dbusSignals.find("Suspend").value().name,
|
||||||
|
this, SLOT(suspend(bool))))
|
||||||
|
{
|
||||||
|
Debug(_log, "Registered for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||||
|
isRegistered = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not register for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRegistered)
|
||||||
|
{
|
||||||
|
_isSuspendRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return isRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerLinux::unregisterOsEventHandler()
|
||||||
|
{
|
||||||
|
if (_isSuspendRegistered)
|
||||||
|
{
|
||||||
|
QDBusConnection systemBus = QDBusConnection::systemBus();
|
||||||
|
if (!systemBus.isConnected())
|
||||||
|
{
|
||||||
|
Info(_log, "The suspend/resume feature is not supported by your system configuration");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QString service = dbusSignals.find("Suspend").value().service;
|
||||||
|
if (systemBus.disconnect(service,
|
||||||
|
dbusSignals.find("Suspend").value().path,
|
||||||
|
dbusSignals.find("Suspend").value().interface,
|
||||||
|
dbusSignals.find("Suspend").value().name,
|
||||||
|
this, SLOT(suspend(bool))))
|
||||||
|
{
|
||||||
|
Debug(_log, "Unregistered for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||||
|
_isSuspendRegistered = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not unregister for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OsEventHandlerLinux::registerLockHandler()
|
||||||
|
{
|
||||||
|
bool isRegistered {_isLockRegistered};
|
||||||
|
|
||||||
|
if (!_isLockRegistered)
|
||||||
|
{
|
||||||
|
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||||
|
if (!sessionBus.isConnected())
|
||||||
|
{
|
||||||
|
Info(_log, "The lock/unlock feature is not supported by your system configuration");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DbusSignalsMap::const_iterator iter = dbusSignals.find("ScreenSaver");
|
||||||
|
while (iter != dbusSignals.end() && iter.key() == "ScreenSaver") {
|
||||||
|
QString service = iter.value().service;
|
||||||
|
if (sessionBus.connect(service,
|
||||||
|
iter.value().path,
|
||||||
|
iter.value().interface,
|
||||||
|
iter.value().name,
|
||||||
|
this, SLOT(lock(bool))))
|
||||||
|
{
|
||||||
|
Debug(_log, "Registered for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||||
|
isRegistered = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not register for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||||
|
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRegistered)
|
||||||
|
{
|
||||||
|
_isLockRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerLinux::unregisterLockHandler()
|
||||||
|
{
|
||||||
|
bool isUnregistered {false};
|
||||||
|
|
||||||
|
if (_isLockRegistered)
|
||||||
|
{
|
||||||
|
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||||
|
if (!sessionBus.isConnected())
|
||||||
|
{
|
||||||
|
Info(_log, "The lock/unlock feature is not supported by your system configuration");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DbusSignalsMap::const_iterator iter = dbusSignals.find("ScreenSaver");
|
||||||
|
while (iter != dbusSignals.end() && iter.key() == "ScreenSaver") {
|
||||||
|
QString service = iter.value().service;
|
||||||
|
if (sessionBus.disconnect(service,
|
||||||
|
iter.value().path,
|
||||||
|
iter.value().interface,
|
||||||
|
iter.value().name,
|
||||||
|
this, SLOT(lock(bool))))
|
||||||
|
{
|
||||||
|
Debug(_log, "Unregistered for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||||
|
isUnregistered = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not unregister for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||||
|
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUnregistered)
|
||||||
|
{
|
||||||
|
_isLockRegistered = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // HYPERION_HAS_DBUS
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
|
OsEventHandlerMacOS::OsEventHandlerMacOS()
|
||||||
|
: _sleepEventHandler(nullptr)
|
||||||
|
, _lockEventHandler(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface SleepEvents : NSObject
|
||||||
|
{
|
||||||
|
OsEventHandlerMacOS *_eventHandler;
|
||||||
|
}
|
||||||
|
- (id)initSleepEvents:(OsEventHandlerMacOS *)osEventHandler;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SleepEvents
|
||||||
|
- (id)initSleepEvents:(OsEventHandlerMacOS *)osEventHandler
|
||||||
|
{
|
||||||
|
if ((self = [super init]))
|
||||||
|
{
|
||||||
|
_eventHandler = osEventHandler;
|
||||||
|
id notifCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||||
|
[notifCenter addObserver:self selector:@selector(receiveSleepWake:) name:NSWorkspaceWillSleepNotification object:nil];
|
||||||
|
[notifCenter addObserver:self selector:@selector(receiveSleepWake:) name:NSWorkspaceDidWakeNotification object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
|
||||||
|
_eventHandler = nullptr;
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) receiveSleepWake:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
if (!_eventHandler) return;
|
||||||
|
if (notification.name == NSWorkspaceWillSleepNotification)
|
||||||
|
{
|
||||||
|
_eventHandler->suspend(true);
|
||||||
|
}
|
||||||
|
else if (notification.name == NSWorkspaceDidWakeNotification)
|
||||||
|
{
|
||||||
|
_eventHandler->suspend(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
bool OsEventHandlerMacOS::registerOsEventHandler()
|
||||||
|
{
|
||||||
|
bool isRegistered {_isSuspendRegistered};
|
||||||
|
if (!_isSuspendRegistered)
|
||||||
|
{
|
||||||
|
_sleepEventHandler = [[SleepEvents alloc] initSleepEvents:this];
|
||||||
|
if (_sleepEventHandler)
|
||||||
|
{
|
||||||
|
Debug(_log, "Registered for suspend/resume events");
|
||||||
|
isRegistered = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not register for suspend/resume events");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRegistered)
|
||||||
|
{
|
||||||
|
_isSuspendRegistered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerMacOS::unregisterOsEventHandler()
|
||||||
|
{
|
||||||
|
if (_isSuspendRegistered && _sleepEventHandler)
|
||||||
|
{
|
||||||
|
[(SleepEvents *)_sleepEventHandler release], _sleepEventHandler = nil;
|
||||||
|
if (!_sleepEventHandler)
|
||||||
|
{
|
||||||
|
Debug(_log, "Unregistered for suspend/resume events");
|
||||||
|
_isSuspendRegistered = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not unregister for suspend/resume events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface LockEvents : NSObject
|
||||||
|
{
|
||||||
|
OsEventHandlerMacOS *_eventHandler;
|
||||||
|
}
|
||||||
|
- (id)initLockEvents:(OsEventHandlerMacOS *)osEventHandler;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation LockEvents
|
||||||
|
- (id)initLockEvents:(OsEventHandlerMacOS *)osEventHandler
|
||||||
|
{
|
||||||
|
if ((self = [super init]))
|
||||||
|
{
|
||||||
|
_eventHandler = osEventHandler;
|
||||||
|
id defCenter = [NSDistributedNotificationCenter defaultCenter];
|
||||||
|
[defCenter addObserver:self selector:@selector(receiveLockUnlock:) name:@"com.apple.screenIsLocked" object:nil];
|
||||||
|
[defCenter addObserver:self selector:@selector(receiveLockUnlock:) name:@"com.apple.screenIsUnlocked" object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
|
||||||
|
_eventHandler = nullptr;
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) receiveLockUnlock:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
if (!_eventHandler) return;
|
||||||
|
if (CFEqual(notification.name, CFSTR("com.apple.screenIsLocked")))
|
||||||
|
{
|
||||||
|
_eventHandler->lock(true);
|
||||||
|
}
|
||||||
|
else if (CFEqual(notification.name, CFSTR("com.apple.screenIsUnlocked")))
|
||||||
|
{
|
||||||
|
_eventHandler->lock(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
bool OsEventHandlerMacOS::registerLockHandler()
|
||||||
|
{
|
||||||
|
bool isRegistered{ _isLockRegistered };
|
||||||
|
if (!_isLockRegistered)
|
||||||
|
{
|
||||||
|
_lockEventHandler = [[LockEvents alloc] initLockEvents:this];
|
||||||
|
if (_lockEventHandler)
|
||||||
|
{
|
||||||
|
Debug(_log, "Registered for lock/unlock events");
|
||||||
|
isRegistered = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not register for lock/unlock events!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRegistered)
|
||||||
|
{
|
||||||
|
_isLockRegistered = true;
|
||||||
|
}
|
||||||
|
return isRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OsEventHandlerMacOS::unregisterLockHandler()
|
||||||
|
{
|
||||||
|
if (_isLockRegistered && _lockEventHandler)
|
||||||
|
{
|
||||||
|
[(LockEvents *)_lockEventHandler release], _lockEventHandler = nil;
|
||||||
|
if (!_lockEventHandler)
|
||||||
|
{
|
||||||
|
Debug(_log, "Unregistered for lock/unlock events");
|
||||||
|
_isLockRegistered = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error(_log, "Could not unregister for lock/unlock events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -15,10 +15,13 @@ VideoWrapper::VideoWrapper()
|
|||||||
{
|
{
|
||||||
// register the image type
|
// register the image type
|
||||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||||
|
qRegisterMetaType<Event>("Event");
|
||||||
|
|
||||||
// Handle the image in the captured thread (Media Foundation/V4L2) using a direct connection
|
// Handle the image in the captured thread (Media Foundation/V4L2) using a direct connection
|
||||||
connect(&_grabber, SIGNAL(newFrame(const Image<ColorRgb>&)), this, SLOT(newFrame(const Image<ColorRgb>&)), Qt::DirectConnection);
|
connect(&_grabber, SIGNAL(newFrame(const Image<ColorRgb>&)), this, SLOT(newFrame(const Image<ColorRgb>&)), Qt::DirectConnection);
|
||||||
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||||
|
|
||||||
|
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoWrapper::~VideoWrapper()
|
VideoWrapper::~VideoWrapper()
|
||||||
@ -37,15 +40,6 @@ void VideoWrapper::stop()
|
|||||||
GrabberWrapper::stop();
|
GrabberWrapper::stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ENABLE_CEC) && !defined(ENABLE_MF)
|
|
||||||
|
|
||||||
void VideoWrapper::handleCecEvent(CECEvent event)
|
|
||||||
{
|
|
||||||
_grabber.handleCecEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||||
{
|
{
|
||||||
if(type == settings::V4L2 && _grabberName.startsWith("V4L2"))
|
if(type == settings::V4L2 && _grabberName.startsWith("V4L2"))
|
||||||
@ -100,11 +94,6 @@ void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument
|
|||||||
obj["hardware_saturation"].toInt(0),
|
obj["hardware_saturation"].toInt(0),
|
||||||
obj["hardware_hue"].toInt(0));
|
obj["hardware_hue"].toInt(0));
|
||||||
|
|
||||||
#if defined(ENABLE_CEC) && defined(ENABLE_V4L2)
|
|
||||||
// CEC Standby
|
|
||||||
_grabber.setCecDetectionEnable(obj["cecDetection"].toBool(true));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Software frame skipping
|
// Software frame skipping
|
||||||
_grabber.setFpsSoftwareDecimation(obj["fpsSoftwareDecimation"].toInt(1));
|
_grabber.setFpsSoftwareDecimation(obj["fpsSoftwareDecimation"].toInt(1));
|
||||||
|
|
||||||
|
@ -79,8 +79,7 @@ V4L2Grabber::V4L2Grabber()
|
|||||||
, _currentFrame(0)
|
, _currentFrame(0)
|
||||||
, _noSignalCounterThreshold(40)
|
, _noSignalCounterThreshold(40)
|
||||||
, _noSignalThresholdColor(ColorRgb{0,0,0})
|
, _noSignalThresholdColor(ColorRgb{0,0,0})
|
||||||
, _cecDetectionEnabled(true)
|
, _standbyActivated(false)
|
||||||
, _cecStandbyActivated(false)
|
|
||||||
, _signalDetectionEnabled(true)
|
, _signalDetectionEnabled(true)
|
||||||
, _noSignalDetected(false)
|
, _noSignalDetected(false)
|
||||||
, _noSignalCounter(0)
|
, _noSignalCounter(0)
|
||||||
@ -1035,7 +1034,7 @@ bool V4L2Grabber::process_image(const void *p, int size)
|
|||||||
|
|
||||||
void V4L2Grabber::newThreadFrame(Image<ColorRgb> image)
|
void V4L2Grabber::newThreadFrame(Image<ColorRgb> image)
|
||||||
{
|
{
|
||||||
if (_cecDetectionEnabled && _cecStandbyActivated)
|
if (_standbyActivated)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_signalDetectionEnabled)
|
if (_signalDetectionEnabled)
|
||||||
@ -1203,16 +1202,6 @@ void V4L2Grabber::setSignalDetectionEnable(bool enable)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void V4L2Grabber::setCecDetectionEnable(bool enable)
|
|
||||||
{
|
|
||||||
if (_cecDetectionEnabled != enable)
|
|
||||||
{
|
|
||||||
_cecDetectionEnabled = enable;
|
|
||||||
if(_initialized)
|
|
||||||
Info(_log, "%s", QSTRING_CSTR(QString("CEC detection is now %1").arg(enable ? "enabled" : "disabled")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool V4L2Grabber::reload(bool force)
|
bool V4L2Grabber::reload(bool force)
|
||||||
{
|
{
|
||||||
if (_reload || force)
|
if (_reload || force)
|
||||||
@ -1231,26 +1220,6 @@ bool V4L2Grabber::reload(bool force)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(ENABLE_CEC)
|
|
||||||
|
|
||||||
void V4L2Grabber::handleCecEvent(CECEvent event)
|
|
||||||
{
|
|
||||||
switch (event)
|
|
||||||
{
|
|
||||||
case CECEvent::On :
|
|
||||||
Debug(_log,"CEC on event received");
|
|
||||||
_cecStandbyActivated = false;
|
|
||||||
return;
|
|
||||||
case CECEvent::Off :
|
|
||||||
Debug(_log,"CEC off event received");
|
|
||||||
_cecStandbyActivated = true;
|
|
||||||
return;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QJsonArray V4L2Grabber::discover(const QJsonObject& params)
|
QJsonArray V4L2Grabber::discover(const QJsonObject& params)
|
||||||
{
|
{
|
||||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||||
|
@ -49,6 +49,7 @@ add_library(hyperion
|
|||||||
|
|
||||||
target_link_libraries(hyperion
|
target_link_libraries(hyperion
|
||||||
blackborder
|
blackborder
|
||||||
|
events
|
||||||
hyperion-utils
|
hyperion-utils
|
||||||
leddevice
|
leddevice
|
||||||
database
|
database
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
// utils includes
|
// utils includes
|
||||||
#include <utils/GlobalSignals.h>
|
#include <utils/GlobalSignals.h>
|
||||||
|
#include <events/EventHandler.h>
|
||||||
|
|
||||||
// qt
|
// qt
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@ -49,6 +50,8 @@ GrabberWrapper::GrabberWrapper(const QString& grabberName, Grabber * ggrabber, i
|
|||||||
|
|
||||||
// listen for source requests
|
// listen for source requests
|
||||||
connect(GlobalSignals::getInstance(), &GlobalSignals::requestSource, this, &GrabberWrapper::handleSourceRequest);
|
connect(GlobalSignals::getInstance(), &GlobalSignals::requestSource, this, &GrabberWrapper::handleSourceRequest);
|
||||||
|
|
||||||
|
QObject::connect(EventHandler::getInstance(), &EventHandler::signalEvent, this, &GrabberWrapper::handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
GrabberWrapper::~GrabberWrapper()
|
GrabberWrapper::~GrabberWrapper()
|
||||||
@ -83,6 +86,11 @@ void GrabberWrapper::stop()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GrabberWrapper::handleEvent(Event event)
|
||||||
|
{
|
||||||
|
_ggrabber->handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
bool GrabberWrapper::isActive() const
|
bool GrabberWrapper::isActive() const
|
||||||
{
|
{
|
||||||
return _timer->isActive();
|
return _timer->isActive();
|
||||||
|
@ -63,23 +63,38 @@ void HyperionIManager::stopAll()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyperionIManager::suspend()
|
void HyperionIManager::handleEvent(Event event)
|
||||||
{
|
{
|
||||||
Info(_log,"Suspend all instances and enabled components");
|
Debug(_log,"%s Event [%d] received", eventToString(event), event);
|
||||||
QMap<quint8, Hyperion*> instCopy = _runningInstances;
|
switch (event) {
|
||||||
for(const auto instance : instCopy)
|
case Event::Suspend:
|
||||||
{
|
toggleSuspend(true);
|
||||||
emit instance->suspendRequest(true);
|
break;
|
||||||
|
|
||||||
|
case Event::Resume:
|
||||||
|
toggleSuspend(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::Idle:
|
||||||
|
toggleIdle(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Event::ResumeIdle:
|
||||||
|
toggleIdle(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyperionIManager::resume()
|
void HyperionIManager::toggleSuspend(bool isSuspend)
|
||||||
{
|
{
|
||||||
Info(_log,"Resume all instances and enabled components");
|
Info(_log,"Put all instances in %s state", isSuspend ? "suspend" : "working");
|
||||||
QMap<quint8, Hyperion*> instCopy = _runningInstances;
|
QMap<quint8, Hyperion*> instCopy = _runningInstances;
|
||||||
for(const auto instance : instCopy)
|
for(const auto instance : instCopy)
|
||||||
{
|
{
|
||||||
emit instance->suspendRequest(false);
|
emit instance->suspendRequest(isSuspend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -894,6 +894,44 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config)
|
|||||||
Debug(_log, "LED-Device records migrated");
|
Debug(_log, "LED-Device records migrated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.contains("cecEvents"))
|
||||||
|
{
|
||||||
|
bool isCECEnabled {false};
|
||||||
|
if (config.contains("grabberV4L2"))
|
||||||
|
{
|
||||||
|
QJsonObject newGrabberV4L2Config = config["grabberV4L2"].toObject();
|
||||||
|
if (newGrabberV4L2Config.contains("cecDetection"))
|
||||||
|
{
|
||||||
|
isCECEnabled = newGrabberV4L2Config.value("cecDetection").toBool(false);
|
||||||
|
newGrabberV4L2Config.remove("cecDetection");
|
||||||
|
config["grabberV4L2"] = newGrabberV4L2Config;
|
||||||
|
|
||||||
|
QJsonObject newGCecEventsConfig = config["cecEvents"].toObject();
|
||||||
|
newGCecEventsConfig["enable"] = isCECEnabled;
|
||||||
|
if (!newGCecEventsConfig.contains("actions"))
|
||||||
|
{
|
||||||
|
QJsonObject action1
|
||||||
|
{
|
||||||
|
{"action", "Suspend"},
|
||||||
|
{"event", "standby"}
|
||||||
|
};
|
||||||
|
QJsonObject action2
|
||||||
|
{
|
||||||
|
{"action", "Resume"},
|
||||||
|
{"event", "set stream path"}
|
||||||
|
};
|
||||||
|
|
||||||
|
QJsonArray actions { action1, action2 };
|
||||||
|
newGCecEventsConfig.insert("actions",actions);
|
||||||
|
}
|
||||||
|
config["cecEvents"] = newGCecEventsConfig;
|
||||||
|
|
||||||
|
migrated = true;
|
||||||
|
Debug(_log, "CEC configuration records migrated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,18 @@
|
|||||||
"leds":
|
"leds":
|
||||||
{
|
{
|
||||||
"$ref": "schema-leds.json"
|
"$ref": "schema-leds.json"
|
||||||
|
},
|
||||||
|
"osEvents":
|
||||||
|
{
|
||||||
|
"$ref": "schema-osEvents.json"
|
||||||
|
},
|
||||||
|
"cecEvents":
|
||||||
|
{
|
||||||
|
"$ref": "schema-cecEvents.json"
|
||||||
|
},
|
||||||
|
"schedEvents":
|
||||||
|
{
|
||||||
|
"$ref": "schema-schedEvents.json"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties" : false
|
"additionalProperties" : false
|
||||||
|
@ -23,5 +23,9 @@
|
|||||||
<file alias="schema-leds.json">schema/schema-leds.json</file>
|
<file alias="schema-leds.json">schema/schema-leds.json</file>
|
||||||
<file alias="schema-instCapture.json">schema/schema-instCapture.json</file>
|
<file alias="schema-instCapture.json">schema/schema-instCapture.json</file>
|
||||||
<file alias="schema-network.json">schema/schema-network.json</file>
|
<file alias="schema-network.json">schema/schema-network.json</file>
|
||||||
|
<file alias="schema-eventActions.json">schema/schema-eventActions.json</file>
|
||||||
|
<file alias="schema-schedEvents.json">schema/schema-schedEvents.json</file>
|
||||||
|
<file alias="schema-osEvents.json">schema/schema-osEvents.json</file>
|
||||||
|
<file alias="schema-cecEvents.json">schema/schema-cecEvents.json</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
95
libsrc/hyperion/schema/schema-cecEvents.json
Normal file
95
libsrc/hyperion/schema/schema-cecEvents.json
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": true,
|
||||||
|
"properties": {
|
||||||
|
"enable": {
|
||||||
|
"type": "boolean",
|
||||||
|
"required": true,
|
||||||
|
"title": "edt_conf_general_enable_title",
|
||||||
|
"default": false,
|
||||||
|
"propertyOrder": 1
|
||||||
|
},
|
||||||
|
"buttonReleaseDelayMs": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "stepper",
|
||||||
|
"title": "edt_conf_cec_button_release_delay_ms_title",
|
||||||
|
"append": "edt_append_ms",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 500,
|
||||||
|
"step": 50,
|
||||||
|
"default": 0,
|
||||||
|
"required": false,
|
||||||
|
"access": "expert",
|
||||||
|
"propertyOrder": 2
|
||||||
|
},
|
||||||
|
"buttonRepeatRateMs": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "stepper",
|
||||||
|
"title": "edt_conf_cec_button_repeat_rate_ms_title",
|
||||||
|
"append": "edt_append_ms",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 250,
|
||||||
|
"step": 10,
|
||||||
|
"default": 0,
|
||||||
|
"required": false,
|
||||||
|
"access": "expert",
|
||||||
|
"propertyOrder": 3
|
||||||
|
},
|
||||||
|
"doubleTapTimeoutMs": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "stepper",
|
||||||
|
"title": "edt_conf_cec_double_tap_timeout_ms_title",
|
||||||
|
"append": "edt_append_ms",
|
||||||
|
"minimum": 50,
|
||||||
|
"maximum": 1000,
|
||||||
|
"step": 50,
|
||||||
|
"default": 200,
|
||||||
|
"required": false,
|
||||||
|
"access": "expert",
|
||||||
|
"propertyOrder": 4
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "edt_conf_cec_actions_header_title",
|
||||||
|
"minItems": 0,
|
||||||
|
"required": false,
|
||||||
|
"propertyOrder": 5,
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": true,
|
||||||
|
"title": "edt_conf_cec_actions_header_item_title",
|
||||||
|
"properties": {
|
||||||
|
"event": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "edt_conf_cec_event_title",
|
||||||
|
"enum": [
|
||||||
|
"standby",
|
||||||
|
"set stream path",
|
||||||
|
"F1(blue)",
|
||||||
|
"F2 (red)",
|
||||||
|
"F3 (green)",
|
||||||
|
"F4 (yellow)"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"enum_titles": [
|
||||||
|
"edt_conf_enum_cec_opcode_standby",
|
||||||
|
"edt_conf_enum_cec_opcode_set stream path",
|
||||||
|
"edt_conf_enum_cec_key_f1_blue",
|
||||||
|
"edt_conf_enum_cec_key_f2_red",
|
||||||
|
"edt_conf_enum_cec_key_f3_green",
|
||||||
|
"edt_conf_enum_cec_key_f4_yellow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"propertyOrder": 1
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"$ref": "schema-eventActions.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
|
25
libsrc/hyperion/schema/schema-eventActions.json
Normal file
25
libsrc/hyperion/schema/schema-eventActions.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"title": "edt_conf_action_title",
|
||||||
|
"enum": [
|
||||||
|
"Suspend",
|
||||||
|
"Resume",
|
||||||
|
"ToggleSuspend",
|
||||||
|
"Idle",
|
||||||
|
"ResumeIdle",
|
||||||
|
"ToggleIdle",
|
||||||
|
"Restart"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"enum_titles": [
|
||||||
|
"edt_conf_enum_action_suspend",
|
||||||
|
"edt_conf_enum_action_resume",
|
||||||
|
"edt_conf_enum_action_toggleSuspend",
|
||||||
|
"edt_conf_enum_action_idle",
|
||||||
|
"edt_conf_enum_action_resumeIdle",
|
||||||
|
"edt_conf_enum_action_toggleIdle",
|
||||||
|
"edt_conf_enum_action_restart"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -212,21 +212,13 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"propertyOrder": 23
|
"propertyOrder": 23
|
||||||
},
|
},
|
||||||
"cecDetection": {
|
|
||||||
"type": "boolean",
|
|
||||||
"title": "edt_conf_v4l2_cecDetection_title",
|
|
||||||
"default": false,
|
|
||||||
"required": true,
|
|
||||||
"access": "advanced",
|
|
||||||
"propertyOrder": 24
|
|
||||||
},
|
|
||||||
"signalDetection": {
|
"signalDetection": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"title": "edt_conf_v4l2_signalDetection_title",
|
"title": "edt_conf_v4l2_signalDetection_title",
|
||||||
"default": false,
|
"default": false,
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 25
|
"propertyOrder": 24
|
||||||
},
|
},
|
||||||
"redSignalThreshold": {
|
"redSignalThreshold": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@ -242,7 +234,7 @@
|
|||||||
},
|
},
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"required": true,
|
"required": true,
|
||||||
"propertyOrder": 26
|
"propertyOrder": 25
|
||||||
},
|
},
|
||||||
"greenSignalThreshold": {
|
"greenSignalThreshold": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@ -258,7 +250,7 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 27
|
"propertyOrder": 26
|
||||||
},
|
},
|
||||||
"blueSignalThreshold": {
|
"blueSignalThreshold": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@ -274,7 +266,7 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 28
|
"propertyOrder": 27
|
||||||
},
|
},
|
||||||
"noSignalCounterThreshold": {
|
"noSignalCounterThreshold": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@ -289,7 +281,7 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 29
|
"propertyOrder": 28
|
||||||
},
|
},
|
||||||
"sDVOffsetMin": {
|
"sDVOffsetMin": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
@ -305,7 +297,7 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 30
|
"propertyOrder": 29
|
||||||
},
|
},
|
||||||
"sDVOffsetMax": {
|
"sDVOffsetMax": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
@ -321,7 +313,7 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 31
|
"propertyOrder": 30
|
||||||
},
|
},
|
||||||
"sDHOffsetMin": {
|
"sDHOffsetMin": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
@ -337,7 +329,7 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 32
|
"propertyOrder": 31
|
||||||
},
|
},
|
||||||
"sDHOffsetMax": {
|
"sDHOffsetMax": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
@ -353,7 +345,7 @@
|
|||||||
},
|
},
|
||||||
"required": true,
|
"required": true,
|
||||||
"access": "expert",
|
"access": "expert",
|
||||||
"propertyOrder": 33
|
"propertyOrder": 32
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
|
34
libsrc/hyperion/schema/schema-osEvents.json
Normal file
34
libsrc/hyperion/schema/schema-osEvents.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"type" : "object",
|
||||||
|
"required" : true,
|
||||||
|
"title" : "edt_conf_os_events_heading_title",
|
||||||
|
"properties": {
|
||||||
|
"suspendEnable": {
|
||||||
|
"type": "boolean",
|
||||||
|
"required": true,
|
||||||
|
"title": "edt_conf_os_events_suspendEnable_title",
|
||||||
|
"default": true,
|
||||||
|
"propertyOrder": 1
|
||||||
|
},
|
||||||
|
"lockEnable": {
|
||||||
|
"type": "boolean",
|
||||||
|
"required": true,
|
||||||
|
"title": "edt_conf_os_events_lockEnable_title",
|
||||||
|
"default": true,
|
||||||
|
"propertyOrder": 2
|
||||||
|
},
|
||||||
|
"suspendOnLockEnable": {
|
||||||
|
"type": "boolean",
|
||||||
|
"required": false,
|
||||||
|
"title": "edt_conf_os_events_suspendOnLockEnable_title",
|
||||||
|
"default": false,
|
||||||
|
"options": {
|
||||||
|
"dependencies": {
|
||||||
|
"lockEnable": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"propertyOrder": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties" : false
|
||||||
|
}
|
40
libsrc/hyperion/schema/schema-schedEvents.json
Normal file
40
libsrc/hyperion/schema/schema-schedEvents.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": true,
|
||||||
|
"properties": {
|
||||||
|
"enable": {
|
||||||
|
"type": "boolean",
|
||||||
|
"required": true,
|
||||||
|
"title": "edt_conf_general_enable_title",
|
||||||
|
"default": false,
|
||||||
|
"propertyOrder": 1
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"type": "array",
|
||||||
|
"title": "edt_conf_sched_actions_header_title",
|
||||||
|
"minItems": 0,
|
||||||
|
"required": false,
|
||||||
|
"propertyOrder": 2,
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": true,
|
||||||
|
"title": "edt_conf_sched_actions_header_item_title",
|
||||||
|
"properties": {
|
||||||
|
"event": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "time",
|
||||||
|
"default": "23:00",
|
||||||
|
"title": "edt_conf_time_event_title",
|
||||||
|
"propertyOrder": 1
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"$ref": "schema-eventActions.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
|
@ -34,7 +34,6 @@ add_executable(${PROJECT_NAME}
|
|||||||
systray.h
|
systray.h
|
||||||
hyperiond.cpp
|
hyperiond.cpp
|
||||||
systray.cpp
|
systray.cpp
|
||||||
SuspendHandler.cpp
|
|
||||||
main.cpp
|
main.cpp
|
||||||
${WIN_RC_FILE}
|
${WIN_RC_FILE}
|
||||||
${MACOS_BUNDLE_RESOURCE_FILES}
|
${MACOS_BUNDLE_RESOURCE_FILES}
|
||||||
@ -48,6 +47,7 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
ssdp
|
ssdp
|
||||||
database
|
database
|
||||||
resources
|
resources
|
||||||
|
events
|
||||||
Qt${QT_VERSION_MAJOR}::Core
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
Qt${QT_VERSION_MAJOR}::Gui
|
||||||
Qt${QT_VERSION_MAJOR}::Network
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
|
@ -1,312 +0,0 @@
|
|||||||
#include "SuspendHandler.h"
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
#include <hyperion/HyperionIManager.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
SuspendHandlerBase::SuspendHandlerBase()
|
|
||||||
: _isSuspended(false)
|
|
||||||
, _isIdle(false)
|
|
||||||
, _isLocked (false)
|
|
||||||
{
|
|
||||||
// Trigger suspend/resume/idle scenarios to be executed by Instance mMnager
|
|
||||||
connect(this, &SuspendHandlerBase::suspendEvent, HyperionIManager::getInstance(), &HyperionIManager::suspend);
|
|
||||||
connect(this, &SuspendHandlerBase::resumeEvent, HyperionIManager::getInstance(), &HyperionIManager::resume);
|
|
||||||
connect(this, &SuspendHandlerBase::lockedEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleIdle);
|
|
||||||
connect(this, &SuspendHandlerBase::idleEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleIdle);
|
|
||||||
|
|
||||||
|
|
||||||
// Listen to suspend/resume/idle events received by the Instance Manager via API
|
|
||||||
connect(HyperionIManager::getInstance(), &HyperionIManager::triggerSuspend, this, QOverload<bool>::of(&SuspendHandler::suspend));
|
|
||||||
connect(HyperionIManager::getInstance(), &HyperionIManager::triggerToggleSuspend, this, &SuspendHandler::toggleSuspend);
|
|
||||||
connect(HyperionIManager::getInstance(), &HyperionIManager::triggerIdle, this, &SuspendHandler::idle);
|
|
||||||
connect(HyperionIManager::getInstance(), &HyperionIManager::triggerToggleIdle, this, &SuspendHandler::toggleIdle);
|
|
||||||
}
|
|
||||||
|
|
||||||
SuspendHandlerBase::~SuspendHandlerBase()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SuspendHandlerBase::suspend()
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "");
|
|
||||||
suspend(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SuspendHandlerBase::suspend(bool sleep)
|
|
||||||
{
|
|
||||||
if (sleep)
|
|
||||||
{
|
|
||||||
if (!_isSuspended)
|
|
||||||
{
|
|
||||||
_isSuspended = true;
|
|
||||||
Info(Logger::getInstance("DAEMON"), "Suspend event received - Hyperion is going to sleep");
|
|
||||||
emit suspendEvent();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Suspend event ignored - already suspended");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_isSuspended || _isIdle)
|
|
||||||
{
|
|
||||||
Info(Logger::getInstance("DAEMON"), "Resume event received - Hyperion is going into working mode");
|
|
||||||
emit resumeEvent();
|
|
||||||
_isSuspended = false;
|
|
||||||
_isIdle = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Resume event ignored - not in suspend nor idle mode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SuspendHandlerBase::resume()
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "");
|
|
||||||
suspend(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SuspendHandlerBase::toggleSuspend()
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Toggle suspend event received");
|
|
||||||
if (!_isSuspended)
|
|
||||||
{
|
|
||||||
suspend(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
suspend(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SuspendHandlerBase::idle(bool isIdle)
|
|
||||||
{
|
|
||||||
if (!_isSuspended)
|
|
||||||
{
|
|
||||||
if (isIdle)
|
|
||||||
{
|
|
||||||
if (!_isIdle)
|
|
||||||
{
|
|
||||||
_isIdle = true;
|
|
||||||
Info(Logger::getInstance("DAEMON"), "Idle event received");
|
|
||||||
emit idleEvent(isIdle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_isIdle)
|
|
||||||
{
|
|
||||||
Info(Logger::getInstance("DAEMON"), "Resume from idle event recevied");
|
|
||||||
emit idleEvent(isIdle);
|
|
||||||
_isIdle = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Idle event ignored - Hyperion is suspended");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SuspendHandlerBase::toggleIdle()
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Toggle idle event received");
|
|
||||||
if (!_isIdle)
|
|
||||||
{
|
|
||||||
idle(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
idle(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SuspendHandlerBase::lock(bool isLocked)
|
|
||||||
{
|
|
||||||
if (!_isSuspended)
|
|
||||||
{
|
|
||||||
if (isLocked)
|
|
||||||
{
|
|
||||||
if (!_isLocked)
|
|
||||||
{
|
|
||||||
_isLocked = true;
|
|
||||||
Info(Logger::getInstance("DAEMON"), "Screen lock event received");
|
|
||||||
emit lockedEvent(isLocked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_isLocked)
|
|
||||||
{
|
|
||||||
Info(Logger::getInstance("DAEMON"), "Screen unlock event received");
|
|
||||||
emit lockedEvent(isLocked);
|
|
||||||
_isLocked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Screen lock event ignored - Hyperion is suspended");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QWidget>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <wtsapi32.h>
|
|
||||||
|
|
||||||
#pragma comment( lib, "wtsapi32.lib" )
|
|
||||||
|
|
||||||
SuspendHandlerWindows::SuspendHandlerWindows()
|
|
||||||
{
|
|
||||||
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
|
||||||
|
|
||||||
_notifyHandle = RegisterSuspendResumeNotification(handle, DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
||||||
if (_notifyHandle != NULL)
|
|
||||||
{
|
|
||||||
QCoreApplication::instance()->installNativeEventFilter(this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Error(Logger::getInstance("DAEMON"), "Could not register for suspend/resume events!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION))
|
|
||||||
{
|
|
||||||
Error(Logger::getInstance("DAEMON"), "Could not register for lock/unlock events!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SuspendHandlerWindows::~SuspendHandlerWindows()
|
|
||||||
{
|
|
||||||
if (_notifyHandle != NULL)
|
|
||||||
{
|
|
||||||
QCoreApplication::instance()->removeNativeEventFilter(this);
|
|
||||||
|
|
||||||
UnregisterSuspendResumeNotification(_notifyHandle);
|
|
||||||
|
|
||||||
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
|
||||||
WTSUnRegisterSessionNotification(handle);
|
|
||||||
}
|
|
||||||
_notifyHandle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
||||||
bool SuspendHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, qintptr* /*result*/)
|
|
||||||
#else
|
|
||||||
bool SuspendHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, long int* /*result*/)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
MSG* msg = static_cast<MSG*>(message);
|
|
||||||
|
|
||||||
switch (msg->message)
|
|
||||||
{
|
|
||||||
case WM_WTSSESSION_CHANGE:
|
|
||||||
switch (msg->wParam)
|
|
||||||
{
|
|
||||||
case WTS_SESSION_LOCK:
|
|
||||||
emit lock(true);
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case WTS_SESSION_UNLOCK:
|
|
||||||
emit lock(false);
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WM_POWERBROADCAST:
|
|
||||||
switch (msg->wParam)
|
|
||||||
{
|
|
||||||
case PBT_APMRESUMESUSPEND:
|
|
||||||
emit suspend(false);
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case PBT_APMSUSPEND:
|
|
||||||
emit suspend(true);
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__linux__) && defined(HYPERION_HAS_DBUS)
|
|
||||||
#include <QDBusConnection>
|
|
||||||
|
|
||||||
struct dBusSignals
|
|
||||||
{
|
|
||||||
QString service;
|
|
||||||
QString path;
|
|
||||||
QString interface;
|
|
||||||
QString name;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef QMultiMap<QString, dBusSignals> DbusSignalsMap;
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
namespace {
|
|
||||||
const DbusSignalsMap dbusSignals = {
|
|
||||||
//system signals
|
|
||||||
{"Suspend", {"org.freedesktop.login1","/org/freedesktop/login1","org.freedesktop.login1.Manager","PrepareForSleep"}},
|
|
||||||
|
|
||||||
//Session signals
|
|
||||||
{"ScreenSaver", {"org.freedesktop.ScreenSaver","/org/freedesktop/ScreenSaver","org.freedesktop.ScreenSaver","ActiveChanged"}},
|
|
||||||
{"ScreenSaver", {"org.gnome.ScreenSaver","/org/gnome/ScreenSaver","org.gnome.ScreenSaver","ActiveChanged"}},
|
|
||||||
};
|
|
||||||
} //End of constants
|
|
||||||
|
|
||||||
SuspendHandlerLinux::SuspendHandlerLinux()
|
|
||||||
{
|
|
||||||
QDBusConnection systemBus = QDBusConnection::systemBus();
|
|
||||||
if (!systemBus.isConnected())
|
|
||||||
{
|
|
||||||
Info(Logger::getInstance("DAEMON"), "The suspend/resume feature is not supported by your system configuration");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QString service = dbusSignals.find("Suspend").value().service;
|
|
||||||
if (!systemBus.connect(service,
|
|
||||||
dbusSignals.find("Suspend").value().path,
|
|
||||||
dbusSignals.find("Suspend").value().interface,
|
|
||||||
dbusSignals.find("Suspend").value().name,
|
|
||||||
this, SLOT(suspend(bool))))
|
|
||||||
Error(Logger::getInstance("DAEMON"), "Could not register for suspend/resume events [%s]!", QSTRING_CSTR(service));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Registered for suspend/resume events [%s].", QSTRING_CSTR(service));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
|
||||||
if (!sessionBus.isConnected())
|
|
||||||
{
|
|
||||||
Info(Logger::getInstance("DAEMON"), "The lock/unlock feature is not supported by your system configuration");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DbusSignalsMap::const_iterator iter = dbusSignals.find("ScreenSaver");
|
|
||||||
while (iter != dbusSignals.end() && iter.key() == "ScreenSaver") {
|
|
||||||
QString service = iter.value().service;
|
|
||||||
if (!sessionBus.connect(service,
|
|
||||||
iter.value().path,
|
|
||||||
iter.value().interface,
|
|
||||||
iter.value().name,
|
|
||||||
this, SLOT(lock(bool))))
|
|
||||||
Error(Logger::getInstance("DAEMON"), "Could not register for lock/unlock events [%s]!", QSTRING_CSTR(service));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug(Logger::getInstance("DAEMON"), "Registered for lock/unlock events [%s].", QSTRING_CSTR(service));
|
|
||||||
}
|
|
||||||
++iter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,79 +0,0 @@
|
|||||||
#ifndef SUSPENDHANDLER_H
|
|
||||||
#define SUSPENDHANDLER_H
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
class SuspendHandlerBase : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
SuspendHandlerBase();
|
|
||||||
virtual ~SuspendHandlerBase() override;
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
|
|
||||||
void suspend(bool sleep);
|
|
||||||
|
|
||||||
void suspend();
|
|
||||||
void resume();
|
|
||||||
void toggleSuspend();
|
|
||||||
|
|
||||||
void idle(bool isIdle);
|
|
||||||
void toggleIdle();
|
|
||||||
|
|
||||||
void lock(bool isLocked);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
|
|
||||||
void suspendEvent();
|
|
||||||
void resumeEvent();
|
|
||||||
void lockedEvent(bool);
|
|
||||||
void idleEvent(bool);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool _isSuspended;
|
|
||||||
bool _isIdle;
|
|
||||||
bool _isLocked;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <QAbstractNativeEventFilter>
|
|
||||||
#include <QAbstractEventDispatcher>
|
|
||||||
#include <QWidget>
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
class SuspendHandlerWindows : public SuspendHandlerBase, public QAbstractNativeEventFilter {
|
|
||||||
|
|
||||||
public:
|
|
||||||
SuspendHandlerWindows();
|
|
||||||
~SuspendHandlerWindows() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
||||||
bool nativeEventFilter(const QByteArray& eventType, void* message, qintptr* result) override;
|
|
||||||
#else
|
|
||||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long int* result) override;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
QWidget _widget;
|
|
||||||
HPOWERNOTIFY _notifyHandle;
|
|
||||||
};
|
|
||||||
|
|
||||||
using SuspendHandler = SuspendHandlerWindows;
|
|
||||||
|
|
||||||
#elif defined(__linux__) && defined(HYPERION_HAS_DBUS)
|
|
||||||
|
|
||||||
class SuspendHandlerLinux : public SuspendHandlerBase {
|
|
||||||
|
|
||||||
public:
|
|
||||||
SuspendHandlerLinux();
|
|
||||||
};
|
|
||||||
|
|
||||||
using SuspendHandler = SuspendHandlerLinux;
|
|
||||||
|
|
||||||
#else
|
|
||||||
using SuspendHandler = SuspendHandlerBase;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // SUSPENDHANDLER_H
|
|
@ -92,12 +92,14 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo
|
|||||||
, _osxGrabber(nullptr)
|
, _osxGrabber(nullptr)
|
||||||
, _qtGrabber(nullptr)
|
, _qtGrabber(nullptr)
|
||||||
, _dxGrabber(nullptr)
|
, _dxGrabber(nullptr)
|
||||||
, _ssdp(nullptr)
|
|
||||||
, _audioGrabber(nullptr)
|
, _audioGrabber(nullptr)
|
||||||
#ifdef ENABLE_CEC
|
, _ssdp(nullptr)
|
||||||
, _cecHandler(nullptr)
|
, _eventHandler(nullptr)
|
||||||
#endif
|
, _osEventHandler(nullptr)
|
||||||
, _suspendHandler(nullptr)
|
, _eventScheduler(nullptr)
|
||||||
|
#ifdef ENABLE_CEC
|
||||||
|
, _cecHandler(nullptr)
|
||||||
|
#endif
|
||||||
, _currVideoMode(VideoMode::VIDEO_2D)
|
, _currVideoMode(VideoMode::VIDEO_2D)
|
||||||
{
|
{
|
||||||
HyperionDaemon::daemon = this;
|
HyperionDaemon::daemon = this;
|
||||||
@ -119,8 +121,6 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo
|
|||||||
handleSettingsUpdate(settings::LOGGER, getSetting(settings::LOGGER));
|
handleSettingsUpdate(settings::LOGGER, getSetting(settings::LOGGER));
|
||||||
}
|
}
|
||||||
|
|
||||||
createCecHandler();
|
|
||||||
|
|
||||||
//Create MdnsBrowser singleton in main tread to ensure thread affinity during destruction
|
//Create MdnsBrowser singleton in main tread to ensure thread affinity during destruction
|
||||||
#ifdef ENABLE_MDNS
|
#ifdef ENABLE_MDNS
|
||||||
MdnsBrowser::getInstance();
|
MdnsBrowser::getInstance();
|
||||||
@ -177,7 +177,8 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo
|
|||||||
// ---- network services -----
|
// ---- network services -----
|
||||||
startNetworkServices();
|
startNetworkServices();
|
||||||
|
|
||||||
_suspendHandler = new SuspendHandler();
|
// init events services
|
||||||
|
startEventServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
HyperionDaemon::~HyperionDaemon()
|
HyperionDaemon::~HyperionDaemon()
|
||||||
@ -234,6 +235,7 @@ void HyperionDaemon::handleInstanceStateChange(InstanceState state, quint8 insta
|
|||||||
#endif
|
#endif
|
||||||
sslWsThread->start();
|
sslWsThread->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InstanceState::H_STOPPED:
|
case InstanceState::H_STOPPED:
|
||||||
@ -262,6 +264,10 @@ void HyperionDaemon::freeObjects()
|
|||||||
{
|
{
|
||||||
Debug(_log, "Cleaning up Hyperion before quit.");
|
Debug(_log, "Cleaning up Hyperion before quit.");
|
||||||
|
|
||||||
|
stopCecHandler();
|
||||||
|
delete _eventScheduler;
|
||||||
|
delete _osEventHandler;
|
||||||
|
|
||||||
#ifdef ENABLE_MDNS
|
#ifdef ENABLE_MDNS
|
||||||
if (_mDNSProvider != nullptr)
|
if (_mDNSProvider != nullptr)
|
||||||
{
|
{
|
||||||
@ -332,20 +338,6 @@ void HyperionDaemon::freeObjects()
|
|||||||
_sslWebserver = nullptr;
|
_sslWebserver = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_CEC
|
|
||||||
if (_cecHandler != nullptr)
|
|
||||||
{
|
|
||||||
auto cecHandlerThread = _cecHandler->thread();
|
|
||||||
cecHandlerThread->quit();
|
|
||||||
cecHandlerThread->wait();
|
|
||||||
delete cecHandlerThread;
|
|
||||||
delete _cecHandler;
|
|
||||||
_cecHandler = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
delete _suspendHandler;
|
|
||||||
|
|
||||||
// stop Hyperions (non blocking)
|
// stop Hyperions (non blocking)
|
||||||
_instanceManager->stopAll();
|
_instanceManager->stopAll();
|
||||||
|
|
||||||
@ -432,6 +424,33 @@ void HyperionDaemon::startNetworkServices()
|
|||||||
ssdpThread->start();
|
ssdpThread->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HyperionDaemon::startEventServices()
|
||||||
|
{
|
||||||
|
if (_eventHandler == nullptr)
|
||||||
|
{
|
||||||
|
_eventHandler = EventHandler::getInstance();
|
||||||
|
Debug(_log, "Hyperion event handler created");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_eventScheduler == nullptr)
|
||||||
|
{
|
||||||
|
_eventScheduler = new EventScheduler();
|
||||||
|
_eventScheduler->handleSettingsUpdate(settings::SCHEDEVENTS, getSetting(settings::SCHEDEVENTS));
|
||||||
|
connect(this, &HyperionDaemon::settingsChanged, _eventScheduler, &EventScheduler::handleSettingsUpdate);
|
||||||
|
Debug(_log, "Hyperion event scheduler created");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_osEventHandler == nullptr)
|
||||||
|
{
|
||||||
|
_osEventHandler = new OsEventHandler();
|
||||||
|
_osEventHandler->handleSettingsUpdate(settings::OSEVENTS, getSetting(settings::OSEVENTS));
|
||||||
|
connect(this, &HyperionDaemon::settingsChanged, _osEventHandler, &OsEventHandler::handleSettingsUpdate);
|
||||||
|
Debug(_log, "Operating System event handler created");
|
||||||
|
}
|
||||||
|
|
||||||
|
startCecHandler();
|
||||||
|
}
|
||||||
|
|
||||||
void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJsonDocument& config)
|
void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJsonDocument& config)
|
||||||
{
|
{
|
||||||
if (settingsType == settings::LOGGER)
|
if (settingsType == settings::LOGGER)
|
||||||
@ -692,19 +711,6 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs
|
|||||||
}
|
}
|
||||||
else if (settingsType == settings::V4L2)
|
else if (settingsType == settings::V4L2)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef ENABLE_CEC
|
|
||||||
const QJsonObject& grabberConfig = config.object();
|
|
||||||
if (_cecHandler != nullptr && grabberConfig["cecDetection"].toBool(false))
|
|
||||||
{
|
|
||||||
QMetaObject::invokeMethod(_cecHandler, "start", Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QMetaObject::invokeMethod(_cecHandler, "stop", Qt::QueuedConnection);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ENABLE_V4L2) || defined(ENABLE_MF)
|
#if defined(ENABLE_V4L2) || defined(ENABLE_MF)
|
||||||
if (_videoGrabber == nullptr)
|
if (_videoGrabber == nullptr)
|
||||||
{
|
{
|
||||||
@ -915,26 +921,41 @@ void HyperionDaemon::createGrabberOsx(const QJsonObject& grabberConfig)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyperionDaemon::createCecHandler()
|
void HyperionDaemon::startCecHandler()
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_V4L2) && defined(ENABLE_CEC)
|
#if defined(ENABLE_CEC)
|
||||||
_cecHandler = new CECHandler;
|
if (_cecHandler == nullptr)
|
||||||
|
{
|
||||||
|
_cecHandler = new CECHandler(getSetting(settings::CECEVENTS));
|
||||||
|
|
||||||
QThread* thread = new QThread(this);
|
QThread* cecHandlerThread = new QThread(this);
|
||||||
thread->setObjectName("CECThread");
|
cecHandlerThread->setObjectName("CECThread");
|
||||||
_cecHandler->moveToThread(thread);
|
_cecHandler->moveToThread(cecHandlerThread);
|
||||||
thread->start();
|
|
||||||
|
|
||||||
connect(_cecHandler, &CECHandler::cecEvent, [&](CECEvent event) {
|
connect(cecHandlerThread, &QThread::started, _cecHandler, &CECHandler::start);
|
||||||
if (_videoGrabber != nullptr)
|
connect(cecHandlerThread, &QThread::finished, _cecHandler, &CECHandler::stop);
|
||||||
{
|
connect(this, &HyperionDaemon::settingsChanged, _cecHandler, &CECHandler::handleSettingsUpdate);
|
||||||
_videoGrabber->handleCecEvent(event);
|
Info(_log, "CEC event handler created");
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Info(_log, "CEC handler created");
|
cecHandlerThread->start();
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
Debug(_log, "The CEC handler is not supported on this platform");
|
Debug(_log, "The CEC handler is not supported on this platform");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HyperionDaemon::stopCecHandler()
|
||||||
|
{
|
||||||
|
#if defined(ENABLE_CEC)
|
||||||
|
if (_cecHandler != nullptr)
|
||||||
|
{
|
||||||
|
auto cecHandlerThread = _cecHandler->thread();
|
||||||
|
cecHandlerThread->quit();
|
||||||
|
cecHandlerThread->wait();
|
||||||
|
delete cecHandlerThread;
|
||||||
|
_cecHandler = nullptr;
|
||||||
|
}
|
||||||
|
Info(_log, "CEC handler stopped");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,9 @@
|
|||||||
#include <utils/settings.h>
|
#include <utils/settings.h>
|
||||||
#include <utils/Components.h>
|
#include <utils/Components.h>
|
||||||
|
|
||||||
#include "SuspendHandler.h"
|
#include <events/EventHandler.h>
|
||||||
|
#include <events/OsEventHandler.h>
|
||||||
|
#include <events/EventScheduler.h>
|
||||||
|
|
||||||
class HyperionIManager;
|
class HyperionIManager;
|
||||||
class SysTray;
|
class SysTray;
|
||||||
@ -103,18 +105,13 @@ class HyperionDaemon : public QObject
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
HyperionDaemon(const QString& rootPath, QObject *parent, bool logLvlOverwrite, bool readonlyMode = false);
|
HyperionDaemon(const QString& rootPath, QObject *parent, bool logLvlOverwrite, bool readonlyMode = false);
|
||||||
~HyperionDaemon();
|
~HyperionDaemon() override;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Get webserver pointer (systray)
|
/// @brief Get webserver pointer (systray)
|
||||||
///
|
///
|
||||||
WebServer *getWebServerInstance() { return _webserver; }
|
WebServer *getWebServerInstance() { return _webserver; }
|
||||||
|
|
||||||
///
|
|
||||||
/// @brief Get suspense handler pointer
|
|
||||||
///
|
|
||||||
SuspendHandler* getSuspendHandlerInstance() { return _suspendHandler; }
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief Get the current videoMode
|
/// @brief Get the current videoMode
|
||||||
///
|
///
|
||||||
@ -126,6 +123,7 @@ public:
|
|||||||
QJsonDocument getSetting(settings::type type) const;
|
QJsonDocument getSetting(settings::type type) const;
|
||||||
|
|
||||||
void startNetworkServices();
|
void startNetworkServices();
|
||||||
|
void startEventServices();
|
||||||
|
|
||||||
static HyperionDaemon* getInstance() { return daemon; }
|
static HyperionDaemon* getInstance() { return daemon; }
|
||||||
static HyperionDaemon* daemon;
|
static HyperionDaemon* daemon;
|
||||||
@ -185,10 +183,12 @@ private:
|
|||||||
void createGrabberX11(const QJsonObject & grabberConfig);
|
void createGrabberX11(const QJsonObject & grabberConfig);
|
||||||
void createGrabberXcb(const QJsonObject & grabberConfig);
|
void createGrabberXcb(const QJsonObject & grabberConfig);
|
||||||
void createGrabberQt(const QJsonObject & grabberConfig);
|
void createGrabberQt(const QJsonObject & grabberConfig);
|
||||||
void createCecHandler();
|
|
||||||
void createGrabberDx(const QJsonObject & grabberConfig);
|
void createGrabberDx(const QJsonObject & grabberConfig);
|
||||||
void createGrabberAudio(const QJsonObject & grabberConfig);
|
void createGrabberAudio(const QJsonObject & grabberConfig);
|
||||||
|
|
||||||
|
void startCecHandler();
|
||||||
|
void stopCecHandler();
|
||||||
|
|
||||||
Logger* _log;
|
Logger* _log;
|
||||||
HyperionIManager* _instanceManager;
|
HyperionIManager* _instanceManager;
|
||||||
AuthManager* _authManager;
|
AuthManager* _authManager;
|
||||||
@ -213,10 +213,12 @@ private:
|
|||||||
DirectXWrapper* _dxGrabber;
|
DirectXWrapper* _dxGrabber;
|
||||||
SSDPHandler* _ssdp;
|
SSDPHandler* _ssdp;
|
||||||
AudioWrapper* _audioGrabber;
|
AudioWrapper* _audioGrabber;
|
||||||
#ifdef ENABLE_CEC
|
EventHandler* _eventHandler;
|
||||||
CECHandler* _cecHandler;
|
OsEventHandler* _osEventHandler;
|
||||||
#endif
|
EventScheduler* _eventScheduler;
|
||||||
SuspendHandler* _suspendHandler;
|
#ifdef ENABLE_CEC
|
||||||
|
CECHandler* _cecHandler;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(ENABLE_FLATBUF_SERVER)
|
#if defined(ENABLE_FLATBUF_SERVER)
|
||||||
FlatBufferServer* _flatBufferServer;
|
FlatBufferServer* _flatBufferServer;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <csignal>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@ -46,40 +45,12 @@
|
|||||||
|
|
||||||
#include "hyperiond.h"
|
#include "hyperiond.h"
|
||||||
#include "systray.h"
|
#include "systray.h"
|
||||||
#include "SuspendHandler.h"
|
#include <events/EventHandler.h>
|
||||||
|
|
||||||
using namespace commandline;
|
using namespace commandline;
|
||||||
|
|
||||||
#define PERM0664 (QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther | QFileDevice::WriteOwner | QFileDevice::WriteGroup)
|
#define PERM0664 (QFileDevice::ReadOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther | QFileDevice::WriteOwner | QFileDevice::WriteGroup)
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
void signal_handler(int signum)
|
|
||||||
{
|
|
||||||
HyperionDaemon* hyperiond = HyperionDaemon::getInstance();
|
|
||||||
SuspendHandler* suspendHandler = hyperiond->getSuspendHandlerInstance();
|
|
||||||
|
|
||||||
if (signum == SIGCHLD)
|
|
||||||
{
|
|
||||||
// only quit when a registered child process is gone
|
|
||||||
// currently this feature is not active ...
|
|
||||||
}
|
|
||||||
else if (signum == SIGUSR1)
|
|
||||||
{
|
|
||||||
if (suspendHandler != nullptr)
|
|
||||||
{
|
|
||||||
suspendHandler->suspend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (signum == SIGUSR2)
|
|
||||||
{
|
|
||||||
if (suspendHandler != nullptr)
|
|
||||||
{
|
|
||||||
suspendHandler->resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QCoreApplication* createApplication(int &argc, char *argv[])
|
QCoreApplication* createApplication(int &argc, char *argv[])
|
||||||
{
|
{
|
||||||
bool isGuiApp = false;
|
bool isGuiApp = false;
|
||||||
@ -150,11 +121,6 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
DefaultSignalHandler::install();
|
DefaultSignalHandler::install();
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
signal(SIGCHLD, signal_handler);
|
|
||||||
signal(SIGUSR1, signal_handler);
|
|
||||||
signal(SIGUSR2, signal_handler);
|
|
||||||
#endif
|
|
||||||
// force the locale
|
// force the locale
|
||||||
setlocale(LC_ALL, "C");
|
setlocale(LC_ALL, "C");
|
||||||
QLocale::setDefault(QLocale::c());
|
QLocale::setDefault(QLocale::c());
|
||||||
|
@ -22,10 +22,11 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <webserver/WebServer.h>
|
#include <webserver/WebServer.h>
|
||||||
#include <hyperion/PriorityMuxer.h>
|
#include <hyperion/PriorityMuxer.h>
|
||||||
|
#include <events/EventHandler.h>
|
||||||
|
|
||||||
#include "hyperiond.h"
|
#include "hyperiond.h"
|
||||||
#include "systray.h"
|
#include "systray.h"
|
||||||
#include "SuspendHandler.h"
|
|
||||||
|
|
||||||
SysTray::SysTray(HyperionDaemon *hyperiond)
|
SysTray::SysTray(HyperionDaemon *hyperiond)
|
||||||
: QWidget()
|
: QWidget()
|
||||||
@ -34,7 +35,6 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
|
|||||||
, _hyperion(nullptr)
|
, _hyperion(nullptr)
|
||||||
, _instanceManager(HyperionIManager::getInstance())
|
, _instanceManager(HyperionIManager::getInstance())
|
||||||
, _webPort(8090)
|
, _webPort(8090)
|
||||||
, _suspendHandler (hyperiond->getSuspendHandlerInstance())
|
|
||||||
{
|
{
|
||||||
Q_INIT_RESOURCE(resources);
|
Q_INIT_RESOURCE(resources);
|
||||||
|
|
||||||
@ -44,6 +44,8 @@ SysTray::SysTray(HyperionDaemon *hyperiond)
|
|||||||
|
|
||||||
// instance changes
|
// instance changes
|
||||||
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &SysTray::handleInstanceStateChange);
|
connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &SysTray::handleInstanceStateChange);
|
||||||
|
|
||||||
|
connect(this, &SysTray::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
SysTray::~SysTray()
|
SysTray::~SysTray()
|
||||||
@ -84,13 +86,15 @@ void SysTray::createTrayIcon()
|
|||||||
restartAction->setIcon(QPixmap(":/restart.svg"));
|
restartAction->setIcon(QPixmap(":/restart.svg"));
|
||||||
connect(restartAction, &QAction::triggered, this , [=](){ Process::restartHyperion(12); });
|
connect(restartAction, &QAction::triggered, this , [=](){ Process::restartHyperion(12); });
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Check if can be done with SystemEvents
|
||||||
suspendAction = new QAction(tr("&Suspend"), this);
|
suspendAction = new QAction(tr("&Suspend"), this);
|
||||||
suspendAction->setIcon(QPixmap(":/suspend.svg"));
|
suspendAction->setIcon(QPixmap(":/suspend.svg"));
|
||||||
connect(suspendAction, &QAction::triggered, _suspendHandler, QOverload<>::of(&SuspendHandler::suspend));
|
connect(suspendAction, &QAction::triggered, this, [this]() { emit signalEvent(Event::Suspend); });
|
||||||
|
|
||||||
resumeAction = new QAction(tr("&Resume"), this);
|
resumeAction = new QAction(tr("&Resume"), this);
|
||||||
resumeAction->setIcon(QPixmap(":/resume.svg"));
|
resumeAction->setIcon(QPixmap(":/resume.svg"));
|
||||||
connect(resumeAction, &QAction::triggered, _suspendHandler, &SuspendHandler::resume);
|
connect(resumeAction, &QAction::triggered, this, [this]() { emit signalEvent(Event::Resume); });
|
||||||
|
|
||||||
colorAction = new QAction(tr("&Color"), this);
|
colorAction = new QAction(tr("&Color"), this);
|
||||||
colorAction->setIcon(QPixmap(":/color.svg"));
|
colorAction->setIcon(QPixmap(":/color.svg"));
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
#include <hyperion/Hyperion.h>
|
#include <hyperion/Hyperion.h>
|
||||||
#include <hyperion/HyperionIManager.h>
|
#include <hyperion/HyperionIManager.h>
|
||||||
#include "SuspendHandler.h"
|
#include <events/EventHandler.h>
|
||||||
|
|
||||||
class HyperionDaemon;
|
class HyperionDaemon;
|
||||||
|
|
||||||
@ -22,13 +22,13 @@ class SysTray : public QWidget
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
SysTray(HyperionDaemon *hyperiond);
|
SysTray(HyperionDaemon *hyperiond);
|
||||||
~SysTray();
|
~SysTray() override;
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void showColorDialog();
|
void showColorDialog();
|
||||||
void setColor(const QColor & color);
|
void setColor(const QColor & color);
|
||||||
void closeEvent(QCloseEvent *event);
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void settings() const;
|
void settings() const;
|
||||||
#if defined(ENABLE_EFFECTENGINE)
|
#if defined(ENABLE_EFFECTENGINE)
|
||||||
void setEffect();
|
void setEffect();
|
||||||
@ -42,13 +42,16 @@ private slots:
|
|||||||
///
|
///
|
||||||
/// @brief is called whenever the webserver changes the port
|
/// @brief is called whenever the webserver changes the port
|
||||||
///
|
///
|
||||||
void webserverPortChanged(quint16 port) { _webPort = port; };
|
void webserverPortChanged(quint16 port) { _webPort = port; }
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @brief is called whenever a hyperion instance state changes
|
/// @brief is called whenever a hyperion instance state changes
|
||||||
///
|
///
|
||||||
void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name);
|
void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void signalEvent(Event event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createTrayIcon();
|
void createTrayIcon();
|
||||||
|
|
||||||
@ -84,6 +87,4 @@ private:
|
|||||||
Hyperion *_hyperion;
|
Hyperion *_hyperion;
|
||||||
HyperionIManager *_instanceManager;
|
HyperionIManager *_instanceManager;
|
||||||
quint16 _webPort;
|
quint16 _webPort;
|
||||||
|
|
||||||
SuspendHandler *_suspendHandler;
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user