From 0d9a8b8a3a4a09609a339f54c7d8a9384c561282 Mon Sep 17 00:00:00 2001 From: LordGrey <48840279+Lord-Grey@users.noreply.github.com> Date: Mon, 6 Nov 2023 21:40:12 +0100 Subject: [PATCH] Refactor event handling incl.CEC --- assets/webconfig/i18n/en.json | 16 +- assets/webconfig/js/content_events.js | 68 +- cmake/Dependencies.cmake | 48 +- config/hyperion.config.json.default | 8 +- help/db/hyperion.db | Bin 0 -> 36864 bytes include/api/JsonAPI.h | 20 +- include/cec/CECEvent.h | 8 - include/cec/CECHandler.h | 13 +- include/db/SettingsTable.h | 2 +- include/events/Event.h | 32 + include/events/EventHandler.h | 54 ++ include/events/OsEventHandler.h | 116 ++++ include/grabber/V4L2Grabber.h | 13 +- include/grabber/VideoWrapper.h | 8 +- include/hyperion/HyperionIManager.h | 39 +- include/utils/settings.h | 9 +- libsrc/CMakeLists.txt | 10 +- libsrc/api/JsonAPI.cpp | 33 +- libsrc/cec/CECHandler.cpp | 197 +++--- libsrc/cec/CMakeLists.txt | 3 + libsrc/events/CMakeLists.txt | 25 + libsrc/events/EventHandler.cpp | 186 +++++ libsrc/events/OsEventHandler.cpp | 448 ++++++++++++ libsrc/grabber/video/VideoWrapper.cpp | 20 +- libsrc/grabber/video/v4l2/V4L2Grabber.cpp | 41 +- libsrc/hyperion/HyperionIManager.cpp | 35 +- libsrc/hyperion/hyperion.schema.json | 10 +- libsrc/hyperion/resource.qrc | 3 +- libsrc/hyperion/schema/schema-cecEvents.json | 17 + ...systemEvents.json => schema-osEvents.json} | 8 +- libsrc/jsonserver/JsonClientConnection.cpp | 4 + src/hyperiond/CMakeLists.txt | 16 +- src/hyperiond/SuspendHandler.cpp | 637 ------------------ src/hyperiond/SuspendHandler.h | 127 ---- src/hyperiond/hyperiond.cpp | 116 ++-- src/hyperiond/hyperiond.h | 17 +- src/hyperiond/main.cpp | 36 +- src/hyperiond/systray.cpp | 11 +- src/hyperiond/systray.h | 10 +- 39 files changed, 1306 insertions(+), 1158 deletions(-) create mode 100644 help/db/hyperion.db delete mode 100644 include/cec/CECEvent.h create mode 100644 include/events/Event.h create mode 100644 include/events/EventHandler.h create mode 100644 include/events/OsEventHandler.h create mode 100644 libsrc/events/CMakeLists.txt create mode 100644 libsrc/events/EventHandler.cpp create mode 100644 libsrc/events/OsEventHandler.cpp create mode 100644 libsrc/hyperion/schema/schema-cecEvents.json rename libsrc/hyperion/schema/{schema-systemEvents.json => schema-osEvents.json} (69%) delete mode 100644 src/hyperiond/SuspendHandler.cpp delete mode 100644 src/hyperiond/SuspendHandler.h diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index c2ff8c2f..f88532d5 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -192,8 +192,8 @@ "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_title": "Token Management", - "conf_system_events_heading_title": "System Events", - "conf_system_events_intro": "Settings related to diffent Operating System 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_webconfig_label_intro": "Webconfiguration settings. Edit wisely.", "dashboard_active_instance": "Selected instance", "dashboard_alert_message_confedit": "Your Hyperion configuration has been modified. To apply it, restart Hyperion.", @@ -457,6 +457,12 @@ "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_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_timeout_expl": "If no data are received for the given period, the component will be (soft) disabled.", "edt_conf_pbs_timeout_title": "Timeout", @@ -477,12 +483,6 @@ "edt_conf_smooth_updateDelay_title": "Output delay", "edt_conf_smooth_updateFrequency_expl": "The output speed to your LED controller.", "edt_conf_smooth_updateFrequency_title": "Update frequency", - "edt_conf_system_events_lockEnable_title": "Listen to lock events", - "edt_conf_system_events_lockEnable_expl": "Listen to screen lock/unlock events", - "edt_conf_system_events_suspendEnable_title": "Listen to suspend events", - "edt_conf_system_events_suspendEnable_expl": "Listen to system suspend/resume events", - "edt_conf_system_events_suspendOnLockEnable_title": "Suspend when locked", - "edt_conf_system_events_suspendOnLockEnable_expl": "Suspend when the screen is locked, otherwise go into idle mode", "edt_conf_v4l2_blueSignalThreshold_expl": "Darkens low blue values (recognized as black)", "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.", diff --git a/assets/webconfig/js/content_events.js b/assets/webconfig/js/content_events.js index ae78b8d6..92ed4d90 100644 --- a/assets/webconfig/js/content_events.js +++ b/assets/webconfig/js/content_events.js @@ -1,35 +1,75 @@ $(document).ready(function () { performTranslation(); - var conf_editor_systemEvents = null; + var CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1); + + var conf_editor_osEvents = null; + var conf_editor_cecEvents = null; + if (window.showOptHelp) { - //System Events - $('#conf_cont').append(createRow('conf_cont_system_events')); - $('#conf_cont_system_events').append(createOptPanel('fa-laptop', $.i18n("conf_system_events_heading_title"), 'editor_container_system_events', 'btn_submit_system_events', 'panel-system')); - $('#conf_cont_system_events').append(createHelpTable(window.schema.systemEvents.properties, $.i18n("conf_system_events_heading_title"))); + //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"))); + + //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_system_events_heading_title"), 'editor_container_system_events', 'btn_submit_system_events')); + $('#conf_cont').append(createOptPanel('fa-laptop', $.i18n("conf_os_events_heading_title"), 'editor_container_os_events', 'btn_submit_os_events')); + if (CEC_ENABLED) { + $('#conf_cont').append(createOptPanel('fa-tv', $.i18n("conf_cec_events_heading_title"), 'editor_container_cec_events', 'btn_submit_cec_events')); + } } - //System Events - conf_editor_systemEvents = createJsonEditor('editor_container_system_events', { - systemEvents: window.schema.systemEvents + //Operating System Events + conf_editor_osEvents = createJsonEditor('editor_container_os_events', { + osEvents: window.schema.osEvents }, true, true); - conf_editor_systemEvents.on('change', function () { - conf_editor_systemEvents.validate().length || window.readOnlyMode ? $('#btn_submit_system_events').prop('disabled', true) : $('#btn_submit_system_events').prop('disabled', false); + 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_system_events').off().on('click', function () { - requestWriteConfig(conf_editor_systemEvents.getValue()); + $('#btn_submit_os_events').off().on('click', function () { + requestWriteConfig(conf_editor_osEvents.getValue()); }); + //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 () { + var 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 () { + requestWriteConfig(conf_editor_cecEvents.getValue()); + }); + } + //create introduction if (window.showOptHelp) { - createHint("intro", $.i18n('conf_system_events_intro'), "editor_container_system_events"); + 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(); diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 40ada3e1..5ef615d5 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -231,26 +231,34 @@ macro(DeployLinux TARGET) if (EXISTS ${QT_PLUGINS_DIR}/${PLUGIN}) file(GLOB files "${QT_PLUGINS_DIR}/${PLUGIN}/*.so") foreach(file ${files}) - get_prerequisites(${file} PLUGINS 0 1 "" "") - foreach(DEPENDENCY ${PLUGINS}) - get_filename_component(resolved ${DEPENDENCY} NAME_WE) - list(FIND SYSTEM_LIBS_SKIP ${resolved} _index) - if (${_index} GREATER -1) - continue() # Skip system libraries - else() - gp_resolve_item("${file}" "${DEPENDENCY}" "" "" resolved_file) - get_filename_component(resolved_file ${resolved_file} ABSOLUTE) - gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) - get_filename_component(file_canonical ${resolved_file} REALPATH) - gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) - endif() - endforeach() - - install( - FILES ${file} - DESTINATION "share/hyperion/lib/${PLUGIN}" - COMPONENT "Hyperion" - ) + if ("${file}" STREQUAL "libqsqlmimer.so") + continue() + else() + get_prerequisites(${file} PLUGINS 0 1 "" "") + foreach(DEPENDENCY ${PLUGINS}) + get_filename_component(resolved ${DEPENDENCY} NAME_WE) + list(FIND SYSTEM_LIBS_SKIP ${resolved} _index) + if (${_index} GREATER -1) + continue() # Skip system libraries + else() + gp_resolve_item("${file}" "${DEPENDENCY}" "" "" resolved_file) + get_filename_component(resolved_file ${resolved_file} ABSOLUTE) + gp_append_unique(PREREQUISITE_LIBS ${resolved_file}) + get_filename_component(file_canonical ${resolved_file} REALPATH) + gp_append_unique(PREREQUISITE_LIBS ${file_canonical}) + endif() + endforeach() + endif() + if ("${file}" STREQUAL "libqsqlmimer.so") + continue() + else() + install( + FILES ${file} + DESTINATION "share/hyperion/lib/${PLUGIN}" + COMPONENT "Hyperion" + ) + endif() + endforeach() endif() endforeach() diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 12325cde..1fb46c93 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -250,8 +250,12 @@ } ], - "systemEvents": { + "osEvents": { "suspendEnable": true, "lockEnable": true - } + }, + + "cecEvents": { + "enable": false + } } diff --git a/help/db/hyperion.db b/help/db/hyperion.db new file mode 100644 index 0000000000000000000000000000000000000000..b7ce72cd6c4b4c9671a1678601ddc73231460987 GIT binary patch literal 36864 zcmeHP&2Jn>b{|q2inJus-b7Ip$KiOqi`Z+D>hBLG#)gt;NdPU$5^X2;3PgWY&-ALN zd$zlWv{q1pq3j^}2a*7}?qz`-0t7)2AXy+Er@iEmLr#0x;~sJfvN;4u@~i5P>5-LTQ(eDT^U3JI*0=Gyg5Toj zYxr=o_=5izDxY~Dzunpj|N1PRo?QKUYi+dplhr@Phvuh|KqG-h0*wS32{aODB+y8p zkw7DXMgollUPuXyPM$jd+G`8rcT&p^NJ7%o4|_@RfBW+7-JSj2&i>A&z1>bx+Ih2+ z-XD_A{_bz@cQ!gs6ncJ-ecR~t@gVl2@W2m~v~&H&{_fS?+Z&zH(6v(H9>5Q!sJE`1 zSUP|1+`@phIv}YKqG-h0*wS33B2$U zP!`(Dmsiwx{TM-&z1=IFYgX(YSurVN29hAjo1(BOnfs!$t%%#I@iFuNS9eV zXe7``ppif$fkpz21R4o65@;mQNT88GBLThy9{uLRnb(fb&_At~s$R>__I2X8s_V$E ztILKhD3)%DimsZ1>*=0us*)sonxGTS7A-@QHQUo9$1x1c_H4&8bo_5Qw(J;^B-+G6 zeO+@^*)d&@xF(8h&jrL(O~W;apn!_xsG2S7z@tG`5=_mLBu{f)h0?G@%Ok3eekrc4 zpd#8t>xOO;Vu^%y#dJ+g5OvoyB~h^@$Fg)$lTl_mnrGMsaW&OcZAUOwQB-YNb%s^%J|BB;8fshaL7s%$!*MPz|^ zs;XGFA-J}oxRNVN4!Bf2$TUtLpzo;eyw9v=hmIqyzIM)5{ zwut8aw2#{=VXwRGSwTWJy4l@|?sgiF@XZfX5)Y%mN^zs*Hm;s@woB9KCFi1c` zMJMQ081ysnLt@3?DZA=&g#@$_JkP$r8ru7?a!3M56*(?pcP8NaEA4aEqKbijQS!l=yVoF%rYHfTa{h zLFojP##vbmp~ZC36hqTLW z*hS~rvs<{G7W*k(hce`t+)q+6c#KF^$f1%wtdOBf;7|!3qk$3*0X(Uo6@eqB&y=%E zZ~VdAEwz_%^P#dQVKN3Ia>wt5Rw9f z`sH>xLFl)AI!VBbTZk=;LOK}=omi?!Cvvmta^vQ~=lagsa98&d(%3+lC;c_&hN~1MqAJjp|3EgpbAo zJi0kduaN+gKQkJRtkmffH^*F}Y(c$u@38Bjv-h&`_Df6OI96M#7o#`%dZ(t+GcDiQ zBZ{?%aiI$&UkFtGla(;^KP5qRkxu3tWIoGkxw+C_@1g5ajPY@+HcPHBJlIe62vd*F z6O27o{4ho6mze~|tIJE9B2EA zqHIOe6%Q*i+mo{{GU45%LxkxV_v}&_J1MrnVTv}Pz%sUqB4)!KV(W*kJJG3o#ATY2 zEpUvk)87ZjOp}6brc}EZ(T|JaD4orEc zZ-pU&4f-93gSre55Ozb$xff7<&eR3LR+!JZ@r4tRM}r{= zv3oGR@z}$wm|Bc`=e~sxpqMKvj9U|Of(NU#X zxciW{Pn+3~FwE~+!y)$i{mjQ;Ft#t#$$=d;tMM*XsjP_F zY14}qF2eN?8$<#}2V%{Ds5OXO$f_~|>BQE;G8l#T!ssZRP}T&V5%*vLs_boa89kn@ z%IIK-vca~_QgeM|#e)t!R<~mZk)4z)wVdB`^cFn$Q|tI=>E*NSAGTgzj9Y7uR{v@B z(=&fl`u~qs{%$c|cG~~a`2UUn|2+Nw@&AJPC2U&3uy1V{SQTH~{HorQY5f1j|IZAt zd~13k{Qs33tqU{#|Ib$t82G~vN)Im!3|NPjA5bn1G6RrW49o^Q^j#7OSS&L`2iVTO zjL^Y!%UO@`N+6hqv|k+0U0Wjw)0P*e@xjpi3|wa$jl_7Rao)0eXSB zO%#l%T}k^4gGcxjNUF{r3n=KQJSodU1vCKtctjzqS40^q^`1VaK;Kg2!hb`Dsv(>_ z)Ij@K`f8c#l#Oj!`gq(wgJ{Dm9Xk)tKQI$Pz#5cMLs~~>`KPrydP!>Ecw1VeVCOUyoj{k@Mw@9L9t`5 zsS_=n4Z-ini5gB)ruXNgL^4F`XtCp$ouEfkPx{Cu>Lw!_$qP<;fYc;^Fd9q_hO6$= z>Hlc0BYka|lfKp@jQt-ajD57ewEo#>nP#75$pSc_d@^B7p_8-77^8FW=~BjMgy&gv z#%OlrGbN3&JV%-kM45Qnv@yC~o<2LQNG7OG_;L7aNc4ho_zEO?WvA*tN1oR#hTs2H zz;aq@%WYtf?*82`J0iIUDPGM$c)a<0~DER@)=pt8%6rGuq7GD04fn0O=zq^$bt^*sDw~_ z9YQjwIhJ=T!`DGn0evqaj6fX(uXm_G7=b#7iA0pL5r(fpD56qfUZ(gugsfJW&+v5+ zy!&C&FNG1PgXq<9R2YF82#xvYL!vNrU46VpV*&xXuD&8w7*`p(4x&-pqv(~&2-HCg zwKD1|BTxgui%KeuFnk?CGAgVyd>ukDE6i&QUkA~tYf~_fEQ>rkpF#5p5x1o#E>cid12q;p-3@ zQoG7V42G|R7;1%iygvs7Y9OYdRhTyzfjUTaIcE4egkn~hHz|HDq9REkmT%TZsU^wh=?2wpan71nMC=wX8}A2-HGMD5(+v_yizq znLGtq!BnRT5IiN!M{qSg7m{rU#XeWvPxuh-uEmG&wnjdJxBYSv*&bbRQr+YE5UwS` zhj8r`K7?l!@ew@3i;Kvtvx51mb;pNrZAds z6h36O8^o_aF$Sw{7Po#@sTlRDCpQhbTloLg*cpBQ|KiENZMFOOr}=3l&`6+>z*j{A z<2PTYZ|>k7eLmxxJJ_}EJc;V(dUMAtJ^PnuJ4@$&dXF6kPF_ZV`{3Nj-L Jzzk% z@j*U>7lh;@SfH}lQ{+mZlaJs=Q~3~X%$5(~MuzziUig`d;D)&Q2o?t~8dkDOB_#9Y<~AZ!&Kxh}0C)-#k0uhUhP=2+ zn2Qj(nw}5g?k9W*ch}-W==ND`C)uty_jm(`<-F~e+XjD+=R>%b1Rp}}6`1+U{{vFE B;uin_ literal 0 HcmV?d00001 diff --git a/include/api/JsonAPI.h b/include/api/JsonAPI.h index 3880fc42..04d43270 100644 --- a/include/api/JsonAPI.h +++ b/include/api/JsonAPI.h @@ -2,6 +2,7 @@ // parent class #include +#include // hyperion includes #include @@ -105,24 +106,9 @@ signals: 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); - - /// - /// 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(); + void signalEvent(Event event); private: // true if further callbacks are forbidden (http) diff --git a/include/cec/CECEvent.h b/include/cec/CECEvent.h deleted file mode 100644 index 1649ad22..00000000 --- a/include/cec/CECEvent.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -enum class CECEvent -{ - On, - Off -}; - diff --git a/include/cec/CECHandler.h b/include/cec/CECHandler.h index e35bdd20..38f12036 100644 --- a/include/cec/CECHandler.h +++ b/include/cec/CECHandler.h @@ -7,7 +7,10 @@ #include -#include +#include + +//#include +#include using CECCallbacks = CEC::ICECCallbacks; using CECAdapter = CEC::ICECAdapter; @@ -39,8 +42,12 @@ public slots: bool start(); void stop(); + virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + signals: - void cecEvent(CECEvent event); + //void cecEvent(CECEvent event); + + void signalEvent(Event event); private: /* CEC Callbacks */ @@ -65,5 +72,7 @@ private: CECCallbacks _cecCallbacks {}; CECConfig _cecConfig {}; + bool _isEnabled; + Logger * _logger {}; }; diff --git a/include/db/SettingsTable.h b/include/db/SettingsTable.h index 4adad496..0c7c790e 100644 --- a/include/db/SettingsTable.h +++ b/include/db/SettingsTable.h @@ -113,7 +113,7 @@ public: // capture << "framegrabber" << "grabberV4L2" << "grabberAudio" //Events - << "systemEvents" + << "osEvents" << "cecEvents" // other << "logger" << "general"; diff --git a/include/events/Event.h b/include/events/Event.h new file mode 100644 index 00000000..18885557 --- /dev/null +++ b/include/events/Event.h @@ -0,0 +1,32 @@ +#ifndef EVENT_H +#define EVENT_H + +enum class Event +{ + 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 detector"; + 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"; + default: return "Unknown"; + } +} + +#endif // EVENT_H diff --git a/include/events/EventHandler.h b/include/events/EventHandler.h new file mode 100644 index 00000000..3ad64420 --- /dev/null +++ b/include/events/EventHandler.h @@ -0,0 +1,54 @@ +#ifndef EVENTHANDLER_H +#define EVENTHANDLER_H + +#include +#include + +#include + +class Logger; + +class EventHandler : public QObject { + Q_OBJECT + +public: + + EventHandler(); + ~EventHandler() override; + + static EventHandler* getInstance(); + +public slots: + + virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + + 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 + diff --git a/include/events/OsEventHandler.h b/include/events/OsEventHandler.h new file mode 100644 index 00000000..7b8952aa --- /dev/null +++ b/include/events/OsEventHandler.h @@ -0,0 +1,116 @@ +#ifndef OsEventHandler_H +#define OsEventHandler_H +#include +#include + +#include + +#if defined(_WIN32) +#include +#include +#include +#include +#endif + +#include + +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 {}; + +private: + +}; + +#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; + +#else +using OsEventHandler = OsEventHandlerBase; +#endif + +#endif // OsEventHandler_H diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index 22f8cdba..341b3e6c 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -24,9 +24,7 @@ // Determine the cmake options #include -#if defined(ENABLE_CEC) - #include -#endif +#include /// /// Capture class for V4L2 devices @@ -77,13 +75,10 @@ public: void setSignalThreshold(double redSignalThreshold, double greenSignalThreshold, double blueSignalThreshold, int noSignalCounterThreshold = 50); void setSignalDetectionOffset( double verticalMin, double horizontalMin, double verticalMax, double horizontalMax); void setSignalDetectionEnable(bool enable); - void setCecDetectionEnable(bool enable); 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 - - /// /// @brief Discover available V4L2 USB devices (for configuration). /// @param[in] params Parameters used to overwrite discovery default behaviour @@ -97,9 +92,7 @@ public slots: void stop(); void newThreadFrame(Image image); -#if defined(ENABLE_CEC) - void handleCecEvent(CECEvent event); -#endif + void handleEvent(Event event); signals: void newFrame(const Image & image); @@ -167,7 +160,7 @@ private: // signal detection int _noSignalCounterThreshold; ColorRgb _noSignalThresholdColor; - bool _cecDetectionEnabled, _cecStandbyActivated, _signalDetectionEnabled, _noSignalDetected; + bool _standbyActivated, _signalDetectionEnabled, _noSignalDetected; int _noSignalCounter; int _brightness, _contrast, _saturation, _hue; double _x_frac_min; diff --git a/include/grabber/VideoWrapper.h b/include/grabber/VideoWrapper.h index 932ff1ab..3338ea1b 100644 --- a/include/grabber/VideoWrapper.h +++ b/include/grabber/VideoWrapper.h @@ -9,9 +9,7 @@ #include #endif -#if defined(ENABLE_CEC) - #include -#endif +#include class VideoWrapper : public GrabberWrapper { @@ -25,9 +23,7 @@ public slots: bool start() override; void stop() override; -#if defined(ENABLE_CEC) - void handleCecEvent(CECEvent event); -#endif + void handleEvent(Event event); void handleSettingsUpdate(settings::type type, const QJsonDocument& config) override; diff --git a/include/hyperion/HyperionIManager.h b/include/hyperion/HyperionIManager.h index b682a696..5b628dc8 100644 --- a/include/hyperion/HyperionIManager.h +++ b/include/hyperion/HyperionIManager.h @@ -5,6 +5,7 @@ #include #include #include +#include // qt #include @@ -74,26 +75,10 @@ public slots: bool stopInstance(quint8 inst); /// - /// @brief Suspend (disable) all Hyperion instances + /// @brief Handle an Hyperion Event + /// @param event Event to be processed /// - void suspend(); - - /// - /// @brief Resume (resume) all Hyperion instances - /// - void resume(); - - /// - /// @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); + void handleEvent(Event event); /// /// @brief Toggle the state of all Hyperion instances @@ -147,10 +132,6 @@ signals: /// void startInstanceResponse(QObject *caller, const int &tan); - void triggerSuspend(bool isSuspend); - void triggerToggleSuspend(); - void triggerIdle(bool isIdle); - void triggerToggleIdle(); signals: /////////////////////////////////////// @@ -192,6 +173,18 @@ private slots: /// 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: friend class HyperionDaemon; /// diff --git a/include/utils/settings.h b/include/utils/settings.h index 64e0d999..f1d36b5c 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -30,7 +30,8 @@ namespace settings { NETWORK, FLATBUFSERVER, PROTOSERVER, - SYSTEMEVENTS, + OSEVENTS, + CECEVENTS, INVALID }; @@ -65,7 +66,8 @@ namespace settings { case NETWORK: return "network"; case FLATBUFSERVER: return "flatbufServer"; case PROTOSERVER: return "protoServer"; - case SYSTEMEVENTS: return "systemEvents"; + case OSEVENTS: return "osEvents"; + case CECEVENTS: return "cecEvents"; default: return "invalid"; } } @@ -99,7 +101,8 @@ namespace settings { else if (type == "network") return NETWORK; else if (type == "flatbufServer") return FLATBUFSERVER; else if (type == "protoServer") return PROTOSERVER; - else if (type == "systemEvents") return SYSTEMEVENTS; + else if (type == "osEvents") return OSEVENTS; + else if (type == "cecEvents") return CECEVENTS; else return INVALID; } } diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index ba5716ba..b80d4253 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -32,6 +32,12 @@ add_subdirectory(db) add_subdirectory(api) add_subdirectory(ssdp) +if(ENABLE_CEC) + add_subdirectory(cec) +endif() + +add_subdirectory(events) + if(ENABLE_MDNS) add_subdirectory(mdns) endif() @@ -41,10 +47,6 @@ if(ENABLE_EFFECTENGINE) add_subdirectory(python) endif() -if(ENABLE_CEC) - add_subdirectory(cec) -endif() - if(ENABLE_EXPERIMENTAL) add_subdirectory(experimental) endif() diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 75863ca2..8cc62761 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -22,6 +22,7 @@ #include #include +#include #if defined(ENABLE_MF) #include @@ -82,6 +83,7 @@ // api includes #include +#include // auth manager #include @@ -110,6 +112,8 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject _ledStreamTimer = new QTimer(this); Q_INIT_RESOURCE(JSONRPC_schemas); + + qRegisterMetaType("Event"); } void JsonAPI::initialize() @@ -135,16 +139,13 @@ void JsonAPI::initialize() connect(this, &JsonAPI::forwardJsonMessage, _hyperion, &Hyperion::forwardJsonMessage); } - //notify instance manager on suspend/resume/idle requests - connect(this, &JsonAPI::suspendAll, _instanceManager, &HyperionIManager::triggerSuspend); - connect(this, &JsonAPI::toggleSuspendAll, _instanceManager, &HyperionIManager::triggerToggleSuspend); - connect(this, &JsonAPI::idleAll, _instanceManager, &HyperionIManager::triggerIdle); - connect(this, &JsonAPI::toggleIdleAll, _instanceManager, &HyperionIManager::triggerToggleIdle); + //notify eventhadler on suspend/resume/idle requests + connect(this, &JsonAPI::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); 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)) { @@ -1014,8 +1015,7 @@ void JsonAPI::handleConfigCommand(const QJsonObject &message, const QString &com if (_adminAuthorized) { Debug(_log, "Restarting due to RPC command"); - - Process::restartHyperion(10); + emit signalEvent(Event::Reload); sendSuccessReply(command + "-" + subcommand, tan); } @@ -1852,32 +1852,37 @@ void JsonAPI::handleSystemCommand(const QJsonObject &message, const QString &com if (subc == "suspend") { - emit suspendAll(true); + emit signalEvent(Event::Suspend); sendSuccessReply(command + "-" + subc, tan); } else if (subc == "resume") { - emit suspendAll(false); + emit signalEvent(Event::Resume); sendSuccessReply(command + "-" + subc, tan); } else if (subc == "restart") { - Process::restartHyperion(11); + emit signalEvent(Event::Restart); sendSuccessReply(command + "-" + subc, tan); } else if (subc == "toggleSuspend") { - emit toggleSuspendAll(); + emit signalEvent(Event::ToggleSuspend); sendSuccessReply(command + "-" + subc, tan); } 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); } else if (subc == "toggleIdle") { - emit toggleIdleAll(); + emit signalEvent(Event::ToggleIdle); sendSuccessReply(command + "-" + subc, tan); } else diff --git a/libsrc/cec/CECHandler.cpp b/libsrc/cec/CECHandler.cpp index 630fe334..5f6c8504 100644 --- a/libsrc/cec/CECHandler.cpp +++ b/libsrc/cec/CECHandler.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -12,13 +13,14 @@ #include /* Enable to turn on detailed CEC logs */ -#define NO_VERBOSE_CEC +#define VERBOSE_CEC CECHandler::CECHandler() + : _isEnabled(false) { - qRegisterMetaType("CECEvent"); + qRegisterMetaType("Event"); - _logger = Logger::getInstance("CEC"); + _logger = Logger::getInstance("CEC"); _cecCallbacks = getCallbacks(); _cecConfig = getConfig(); @@ -31,25 +33,41 @@ CECHandler::~CECHandler() stop(); } +void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +{ + if(type == settings::CECEVENTS) + { + const QJsonObject& obj = config.object(); + + _isEnabled = obj["Enable"].toBool(false); + + Debug(_logger, "_isEnabled: [%d]", _isEnabled); + } +} + bool CECHandler::start() { if (_cecAdapter) - return true; - - 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"); + return true; + } + + // std::string library = std::string("" CEC_LIBRARY); + // _cecAdapter = LibCecInitialise(&_cecConfig, QFile::exists(QString::fromStdString(library)) ? library.c_str() :CEC_DEVICE_TYPE_PLAYBACK_DEVICE nullptr); + + _cecAdapter = LibCecInitialise(&_cecConfig); + if(_cecAdapter == nullptr) + { + Error(_logger, "Failed loading libCEC library. CEC is not supported."); return false; } Info(_logger, "CEC handler started"); - auto adapters = getAdapters(); + const auto adapters = getAdapters(); if (adapters.isEmpty()) { - Error(_logger, "Failed to find CEC adapter"); + Error(_logger, "Failed to find any CEC adapter."); UnloadLibCec(_cecAdapter); _cecAdapter = nullptr; @@ -64,14 +82,19 @@ bool CECHandler::start() if (!opened && openAdapter(adapter)) { - Info(_logger, "CEC Handler initialized with adapter : %s", adapter.strComName); + QObject::connect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); + Info(_logger, "CEC adapter '%s', type: %s initialized." , adapter.strComName, _cecAdapter->ToString(adapter.adapterType)); opened = true; + break; } } + scan(); + if (!opened) { + Error(_logger, "Could not initialize any CEC adapter."); UnloadLibCec(_cecAdapter); _cecAdapter = nullptr; } @@ -81,10 +104,12 @@ bool CECHandler::start() void CECHandler::stop() { - if (_cecAdapter) + if (_cecAdapter != nullptr) { Info(_logger, "Stopping CEC handler"); + QObject::disconnect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); + _cecAdapter->Close(); UnloadLibCec(_cecAdapter); _cecAdapter = nullptr; @@ -97,9 +122,9 @@ CECConfig CECHandler::getConfig() const const std::string name("HyperionCEC"); name.copy(configuration.strDeviceName, std::min(name.size(), sizeof(configuration.strDeviceName))); - configuration.deviceTypes.Add(CEC::CEC_DEVICE_TYPE_RECORDING_DEVICE); configuration.clientVersion = CEC::LIBCEC_VERSION_CURRENT; + configuration.bActivateSource = 0; return configuration; } @@ -125,7 +150,8 @@ QVector CECHandler::getAdapters() const return {}; QVector descriptors(16); - int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), descriptors.size(), nullptr, true /*quickscan*/); + //int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), static_cast(descriptors.size()), nullptr, true /*quickscan*/); + int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), static_cast(descriptors.size()), nullptr, false /*NO quickscan*/); descriptors.resize(size); return descriptors; @@ -138,10 +164,7 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor) if(!_cecAdapter->Open(descriptor.strComName)) { - Error(_logger, "%s", QSTRING_CSTR(QString("Failed to open the CEC adaper on port %1") - .arg(descriptor.strComName)) - ); - + Error(_logger, "CEC adapter '%s', type: %s failed to open.", descriptor.strComName, _cecAdapter->ToString(descriptor.adapterType)); return false; } return true; @@ -149,9 +172,16 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor) void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const { - Info(_logger, "%s", QSTRING_CSTR(QString("CEC Adapter:"))); - Info(_logger, "%s", QSTRING_CSTR(QString("\tName : %1").arg(descriptor.strComName))); - Info(_logger, "%s", QSTRING_CSTR(QString("\tPath : %1").arg(descriptor.strComPath))); + Info(_logger, "CEC Adapter:"); + Info(_logger, "\tName : %s", descriptor.strComName); + Info(_logger, "\tPath : %s", descriptor.strComPath); + Info(_logger, "\tVendor id: %04x", descriptor.iVendorId); + Info(_logger, "\tProduct id: %04x", descriptor.iProductId); + Info(_logger, "\tFirmware id: %d", descriptor.iFirmwareVersion); + if (descriptor.adapterType != CEC::ADAPTERTYPE_UNKNOWN) + { + Info(_logger, "\tType : %s", _cecAdapter->ToString(descriptor.adapterType)); + } } QString CECHandler::scan() const @@ -163,14 +193,14 @@ QString CECHandler::scan() const QJsonArray devices; 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(address); QJsonObject device; - CECVendorId vendor = (CECVendorId)_cecAdapter->GetDeviceVendorId(logicalAddress); + CECVendorId vendor = static_cast(_cecAdapter->GetDeviceVendorId(logicalAddress)); CECPowerStatus power = _cecAdapter->GetDevicePowerStatus(logicalAddress); device["name" ] = _cecAdapter->GetDeviceOSDName(logicalAddress).c_str(); @@ -181,14 +211,17 @@ QString CECHandler::scan() const devices << device; Info(_logger, "%s", QSTRING_CSTR(QString("\tCECDevice: %1 / %2 / %3 / %4") - .arg(device["name"].toString(), - device["vendor"].toString(), - device["address"].toString(), - device["power"].toString())) + .arg(device["name"].toString(), + device["vendor"].toString(), + device["address"].toString(), + device["power"].toString()) + ) ); } } + std::cout << "Devices: " << QJsonDocument(devices).toJson().toStdString() << std::endl; + return QJsonDocument(devices).toJson(QJsonDocument::Compact); } @@ -202,25 +235,17 @@ void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message) switch (message->level) { case CEC::CEC_LOG_ERROR: - Error(handler->_logger, QString("%1") - .arg(message->message) - .toLocal8Bit()); + Error(handler->_logger, "%s", message->message); break; case CEC::CEC_LOG_WARNING: - Warning(handler->_logger, QString("%1") - .arg(message->message) - .toLocal8Bit()); + Warning(handler->_logger, "%s", message->message); break; case CEC::CEC_LOG_TRAFFIC: case CEC::CEC_LOG_NOTICE: - Info(handler->_logger, QString("%1") - .arg(message->message) - .toLocal8Bit()); + Info(handler->_logger, "%s", message->message); break; case CEC::CEC_LOG_DEBUG: - Debug(handler->_logger, QString("%1") - .arg(message->message) - .toLocal8Bit()); + Debug(handler->_logger, "%s", message->message); break; default: break; @@ -230,29 +255,44 @@ void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message) void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key) { -#ifdef VERBOSE_CEC CECHandler * handler = qobject_cast(static_cast(context)); if (!handler) return; CECAdapter * adapter = handler->_cecAdapter; - Debug(handler->_logger, QString("CECHandler::onCecKeyPress: %1") - .arg(adapter->ToString(key->keycode)) - .toLocal8Bit()); +#ifdef VERBOSE_CEC + Debug(handler->_logger, "CECHandler::onCecKeyPress: %s", adapter->ToString(key->keycode)); #endif + + switch (key->keycode) { + case CEC::CEC_USER_CONTROL_CODE_F1_BLUE: + emit handler->signalEvent(Event::ToggleIdle); + break; + case CEC::CEC_USER_CONTROL_CODE_F2_RED: + emit handler->signalEvent(Event::Suspend); + break; + case CEC::CEC_USER_CONTROL_CODE_F3_GREEN: + emit handler->signalEvent(Event::Resume); + break; + + case CEC::CEC_USER_CONTROL_CODE_F4_YELLOW: + emit handler->signalEvent(Event::ToggleSuspend); + 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 CECHandler * handler = qobject_cast(static_cast(context)); if (!handler) return; - Error(handler->_logger, QString("CECHandler::onCecAlert: %1") - .arg(alert) - .toLocal8Bit()); + Error(handler->_logger, QSTRING_CSTR(QString("CECHandler::onCecAlert: %1") + .arg(alert))); #endif } @@ -263,9 +303,7 @@ void CECHandler::onCecConfigurationChanged(void * context, const CECConfig * con if (!handler) return; - Debug(handler->_logger, QString("CECHandler::onCecConfigurationChanged: %1") - .arg(configuration->strDeviceName) - .toLocal8Bit()); + Debug(handler->_logger, "CECHandler::onCecConfigurationChanged: %s", configuration->strDeviceName); #endif } @@ -278,9 +316,7 @@ int CECHandler::onCecMenuStateChanged(void * context, const CECMenuState state) CECAdapter * adapter = handler->_cecAdapter; - Debug(handler->_logger, QString("CECHandler::onCecMenuStateChanged: %1") - .arg(adapter->ToString(state)) - .toLocal8Bit()); + Debug(handler->_logger, "CECHandler::onCecMenuStateChanged: %s", adapter->ToString(state)); #endif return 0; } @@ -294,28 +330,28 @@ void CECHandler::onCecCommandReceived(void * context, const CECCommand * command CECAdapter * adapter = handler->_cecAdapter; #ifdef VERBOSE_CEC - Debug(handler->_logger, QString("CECHandler::onCecCommandReceived: %1 (%2 > %3)") - .arg(adapter->ToString(command->opcode)) - .arg(adapter->ToString(command->initiator)) - .arg(adapter->ToString(command->destination)) - .toLocal8Bit()); + Debug(handler->_logger, "CECHandler::onCecCommandReceived: %s %s > %s)", + adapter->ToString(command->opcode), + adapter->ToString(command->initiator), + adapter->ToString(command->destination) + ); #endif /* We do NOT check sender */ - // if (address == CEC::CECDEVICE_TV) + //if (address == CEC::CECDEVICE_TV) { - if (command->opcode == CEC::CEC_OPCODE_SET_STREAM_PATH) - { - Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source activated: %1") - .arg(adapter->ToString(command->initiator))) - ); - emit handler->cecEvent(CECEvent::On); - } - if (command->opcode == CEC::CEC_OPCODE_STANDBY) - { - Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source deactivated: %1") - .arg(adapter->ToString(command->initiator))) - ); - emit handler->cecEvent(CECEvent::Off); + switch (command->opcode) { + case CEC::CEC_OPCODE_STANDBY: + Info(handler->_logger, "CEC source deactivated: %s", adapter->ToString(command->initiator)); + emit handler->signalEvent(Event::Suspend); + break; + + case CEC::CEC_OPCODE_SET_STREAM_PATH: + Info(handler->_logger, "'CEC source activated: %s", adapter->ToString(command->initiator)); + emit handler->signalEvent(Event::Resume); + break; + + default: + break; } } } @@ -328,14 +364,15 @@ void CECHandler::onCecSourceActivated(void * context, const CECLogicalAddress ad #ifdef VERBOSE_CEC CECHandler * handler = qobject_cast(static_cast(context)); if (!handler) + { return; + } CECAdapter * adapter = handler->_cecAdapter; - - Debug(handler->_logger, QString("CEC source %1 : %2") - .arg(activated ? "activated" : "deactivated") - .arg(adapter->ToString(address)) - .toLocal8Bit()); + Debug(handler->_logger, QSTRING_CSTR(QString("CEC source %1 : %2") + .arg(activated ? "activated" : "deactivated", + adapter->ToString(address)) + )); #endif } diff --git a/libsrc/cec/CMakeLists.txt b/libsrc/cec/CMakeLists.txt index dfd80e7d..5c91eca3 100644 --- a/libsrc/cec/CMakeLists.txt +++ b/libsrc/cec/CMakeLists.txt @@ -7,6 +7,9 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/cec) FILE (GLOB CEC_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp") add_library(cechandler ${CEC_SOURCES}) + +message(STATUS "CEC_LIBRARIES = ${CEC_LIBRARIES}") + list(GET CEC_LIBRARIES 0 CEC_LIBRARIES) add_definitions(-DCEC_LIBRARY="${CEC_LIBRARIES}") diff --git a/libsrc/events/CMakeLists.txt b/libsrc/events/CMakeLists.txt new file mode 100644 index 00000000..12fef45e --- /dev/null +++ b/libsrc/events/CMakeLists.txt @@ -0,0 +1,25 @@ +# Define the current source/header locations +SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/events) +SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/events) + +add_library(events + ${CURRENT_HEADER_DIR}/Event.h + ${CURRENT_HEADER_DIR}/EventHandler.h + ${CURRENT_SOURCE_DIR}/EventHandler.cpp + ${CURRENT_HEADER_DIR}/OsEventHandler.h + ${CURRENT_SOURCE_DIR}/OsEventHandler.cpp +) + +if (UNIX) + 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) + if (NOT APPLE) + target_compile_definitions(events PUBLIC HYPERION_HAS_DBUS) + endif() + endif() +endif(UNIX) + +target_include_directories(events PUBLIC + ${CURRENT_HEADER_DIR} +) diff --git a/libsrc/events/EventHandler.cpp b/libsrc/events/EventHandler.cpp new file mode 100644 index 00000000..d980db04 --- /dev/null +++ b/libsrc/events/EventHandler.cpp @@ -0,0 +1,186 @@ +#include + +#include +#include + +#include + +#include +#include +#include + +EventHandler::EventHandler() + : _isSuspended(false) + , _isIdle(false) +{ + qRegisterMetaType("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::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +{ + if(type == settings::OSEVENTS) + { + const QJsonObject& obj = config.object(); + } +} + +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) +{ + Debug(_log,"%s Event [%d] received", eventToString(event), event); + switch (event) { + case Event::Suspend: + suspend(); + break; + + case Event::Resume: + resume(); + break; + + case Event::ToggleSuspend: + suspend(); + 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; + } +} diff --git a/libsrc/events/OsEventHandler.cpp b/libsrc/events/OsEventHandler.cpp new file mode 100644 index 00000000..c4771439 --- /dev/null +++ b/libsrc/events/OsEventHandler.cpp @@ -0,0 +1,448 @@ +#include "OsEventHandler.h" + +#include +#include +#include + +#include +#include + +#include + +#if defined(_WIN32) +#include +#include +#include +#include + +#pragma comment( lib, "wtsapi32.lib" ) +#endif + +OsEventHandlerBase::OsEventHandlerBase() + : _isSuspendEnabled(false) + , _isLockEnabled(false) + , _isSuspendOnLock(false) + , _isSuspendRegistered(false) + , _isLockRegistered(false) +{ + qRegisterMetaType("Event"); + _log = Logger::getInstance("EVENTS"); + + QObject::connect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); +} + +OsEventHandlerBase::~OsEventHandlerBase() +{ + QObject::disconnect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); + + unregisterLockHandler(); + unregisterOsEventHandler(); +} + +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(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 (_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 (_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 (_widget.winId()); + WTSUnRegisterSessionNotification(handle); + _isLockRegistered = false; + } +} + +#elif defined(__linux__) + +#include + +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 + +struct dBusSignals +{ + QString service; + QString path; + QString interface; + QString name; +}; + +typedef QMultiMap 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 + +#endif // __linux__ diff --git a/libsrc/grabber/video/VideoWrapper.cpp b/libsrc/grabber/video/VideoWrapper.cpp index 7a3ed201..97ab0996 100644 --- a/libsrc/grabber/video/VideoWrapper.cpp +++ b/libsrc/grabber/video/VideoWrapper.cpp @@ -2,6 +2,8 @@ #include +#include + // qt includes #include @@ -15,10 +17,15 @@ VideoWrapper::VideoWrapper() { // register the image type qRegisterMetaType>("Image"); + qRegisterMetaType("Event"); // Handle the image in the captured thread (Media Foundation/V4L2) using a direct connection connect(&_grabber, SIGNAL(newFrame(const Image&)), this, SLOT(newFrame(const Image&)), 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); + + QObject::connect(EventHandler::getInstance(), &EventHandler::signalEvent, this, &VideoWrapper::handleEvent); } VideoWrapper::~VideoWrapper() @@ -37,15 +44,11 @@ void VideoWrapper::stop() GrabberWrapper::stop(); } -#if defined(ENABLE_CEC) && !defined(ENABLE_MF) - -void VideoWrapper::handleCecEvent(CECEvent event) +void VideoWrapper::handleEvent(Event event) { - _grabber.handleCecEvent(event); + _grabber.handleEvent(event); } -#endif - void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { if(type == settings::V4L2 && _grabberName.startsWith("V4L2")) @@ -100,11 +103,6 @@ void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument obj["hardware_saturation"].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 _grabber.setFpsSoftwareDecimation(obj["fpsSoftwareDecimation"].toInt(1)); diff --git a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp index 8f504464..a55adfdb 100644 --- a/libsrc/grabber/video/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/video/v4l2/V4L2Grabber.cpp @@ -79,8 +79,7 @@ V4L2Grabber::V4L2Grabber() , _currentFrame(0) , _noSignalCounterThreshold(40) , _noSignalThresholdColor(ColorRgb{0,0,0}) - , _cecDetectionEnabled(true) - , _cecStandbyActivated(false) + , _standbyActivated(false) , _signalDetectionEnabled(true) , _noSignalDetected(false) , _noSignalCounter(0) @@ -1035,7 +1034,7 @@ bool V4L2Grabber::process_image(const void *p, int size) void V4L2Grabber::newThreadFrame(Image image) { - if (_cecDetectionEnabled && _cecStandbyActivated) + if (_standbyActivated) return; 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) { if (_reload || force) @@ -1231,26 +1220,24 @@ bool V4L2Grabber::reload(bool force) return false; } -#if defined(ENABLE_CEC) - -void V4L2Grabber::handleCecEvent(CECEvent event) +void V4L2Grabber::handleEvent(Event 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; + case Event::Suspend: + case Event::Idle: + Debug(_log,"Suspend/Idle event received"); + _standbyActivated = true; + return; + case Event::Resume: + case Event::ResumeIdle: + Debug(_log,"Resume event received"); + _standbyActivated = false; + return; + default: break; } } -#endif - QJsonArray V4L2Grabber::discover(const QJsonObject& params) { DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData()); diff --git a/libsrc/hyperion/HyperionIManager.cpp b/libsrc/hyperion/HyperionIManager.cpp index 64ca08d2..c5a9d9e6 100644 --- a/libsrc/hyperion/HyperionIManager.cpp +++ b/libsrc/hyperion/HyperionIManager.cpp @@ -63,23 +63,28 @@ void HyperionIManager::stopAll() } } -void HyperionIManager::suspend() +void HyperionIManager::handleEvent(Event event) { - Info(_log,"Suspend all instances and enabled components"); - QMap instCopy = _runningInstances; - for(const auto instance : instCopy) - { - emit instance->suspendRequest(true); - } -} + Debug(_log,"%s Event [%d] received", eventToString(event), event); + switch (event) { + case Event::Suspend: + toggleSuspend(true); + break; -void HyperionIManager::resume() -{ - Info(_log,"Resume all instances and enabled components"); - QMap instCopy = _runningInstances; - for(const auto instance : instCopy) - { - emit instance->suspendRequest(false); + case Event::Resume: + toggleSuspend(false); + break; + + case Event::Idle: + toggleIdle(true); + break; + + case Event::ResumeIdle: + toggleIdle(false); + break; + + default: + break; } } diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index 1fa52c8d..e874190a 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -91,10 +91,14 @@ { "$ref": "schema-leds.json" }, - "systemEvents": + "osEvents": { - "$ref": "schema-systemEvents.json" - } + "$ref": "schema-osEvents.json" + }, + "cecEvents": + { + "$ref": "schema-cecEvents.json" + } }, "additionalProperties" : false } diff --git a/libsrc/hyperion/resource.qrc b/libsrc/hyperion/resource.qrc index f7ea7cad..9627856a 100644 --- a/libsrc/hyperion/resource.qrc +++ b/libsrc/hyperion/resource.qrc @@ -23,6 +23,7 @@ schema/schema-leds.json schema/schema-instCapture.json schema/schema-network.json - schema/schema-systemEvents.json + schema/schema-osEvents.json + schema/schema-cecEvents.json diff --git a/libsrc/hyperion/schema/schema-cecEvents.json b/libsrc/hyperion/schema/schema-cecEvents.json new file mode 100644 index 00000000..26c69dc4 --- /dev/null +++ b/libsrc/hyperion/schema/schema-cecEvents.json @@ -0,0 +1,17 @@ +{ + "type" : "object", + "required" : true, + "title" : "edt_conf_cec_events_heading_title", + "properties" : + { + "enable" : + { + "type" : "boolean", + "required" : true, + "title" : "edt_conf_general_enable_title", + "default" : false, + "propertyOrder" : 1 + } + }, + "additionalProperties" : false +} diff --git a/libsrc/hyperion/schema/schema-systemEvents.json b/libsrc/hyperion/schema/schema-osEvents.json similarity index 69% rename from libsrc/hyperion/schema/schema-systemEvents.json rename to libsrc/hyperion/schema/schema-osEvents.json index 6563e7da..9adc9161 100644 --- a/libsrc/hyperion/schema/schema-systemEvents.json +++ b/libsrc/hyperion/schema/schema-osEvents.json @@ -1,26 +1,26 @@ { "type" : "object", "required" : true, - "title" : "edt_conf_system_events_heading_title", + "title" : "edt_conf_os_events_heading_title", "properties": { "suspendEnable": { "type": "boolean", "required": true, - "title": "edt_conf_system_events_suspendEnable_title", + "title": "edt_conf_os_events_suspendEnable_title", "default": true, "propertyOrder": 1 }, "lockEnable": { "type": "boolean", "required": true, - "title": "edt_conf_system_events_lockEnable_title", + "title": "edt_conf_os_events_lockEnable_title", "default": true, "propertyOrder": 2 }, "suspendOnLockEnable": { "type": "boolean", "required": false, - "title": "edt_conf_system_events_suspendOnLockEnable_title", + "title": "edt_conf_os_events_suspendOnLockEnable_title", "default": false, "options": { "dependencies": { diff --git a/libsrc/jsonserver/JsonClientConnection.cpp b/libsrc/jsonserver/JsonClientConnection.cpp index 8af1346a..6c04574e 100644 --- a/libsrc/jsonserver/JsonClientConnection.cpp +++ b/libsrc/jsonserver/JsonClientConnection.cpp @@ -36,6 +36,8 @@ void JsonClientConnection::readRequest() // remove message data from buffer _receiveBuffer = _receiveBuffer.mid(bytes); + std::cout << "JsonClientConnection::readRequest | [" << _socket->peerAddress().toString().toStdString() << "] Received: [" << message.toStdString() << "]" << std::endl; + // handle message _jsonAPI->handleMessage(message); @@ -49,6 +51,8 @@ qint64 JsonClientConnection::sendMessage(QJsonObject message) QJsonDocument writer(message); QByteArray data = writer.toJson(QJsonDocument::Compact) + "\n"; + std::cout << "JsonClientConnection::sendMessage | [" << _socket->peerAddress().toString().toStdString() << "] Send: [" << data.constData() << "]" << std::endl; + if (!_socket || (_socket->state() != QAbstractSocket::ConnectedState)) return 0; return _socket->write(data.data(), data.size()); } diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index 35326c22..66ca3482 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -30,29 +30,17 @@ if (APPLE) set_source_files_properties(${BUNDLE_RESOURCE_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif(APPLE) -if (UNIX) - find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus QUIET ) - if (Qt${QT_VERSION_MAJOR}DBus_FOUND) - set(hyperiond_POWER_MNG_DBUS "Qt${QT_VERSION_MAJOR}::DBus") - endif() -endif(UNIX) - add_executable(${PROJECT_NAME} console.h hyperiond.h systray.h hyperiond.cpp systray.cpp - SuspendHandler.cpp main.cpp ${hyperiond_WIN_RC_PATH} ${BUNDLE_RESOURCE_FILES} ) -if (UNIX AND NOT APPLE AND Qt${QT_VERSION_MAJOR}DBus_FOUND) - target_compile_definitions(${PROJECT_NAME} PUBLIC HYPERION_HAS_DBUS) -endif() - # promote hyperiond as GUI app if (WIN32) target_link_options(${PROJECT_NAME} PUBLIC /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup) @@ -66,11 +54,11 @@ target_link_libraries(${PROJECT_NAME} ssdp database resources + events Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets - ${hyperiond_POWER_MNG_DBUS} ) if(ENABLE_EFFECTENGINE) @@ -119,7 +107,7 @@ if (ENABLE_MF) endif (ENABLE_MF) if (ENABLE_AUDIO) - target_link_libraries(hyperiond audio-grabber) + target_link_libraries(${PROJECT_NAME} audio-grabber) endif() if (ENABLE_AMLOGIC) diff --git a/src/hyperiond/SuspendHandler.cpp b/src/hyperiond/SuspendHandler.cpp deleted file mode 100644 index d6a437e1..00000000 --- a/src/hyperiond/SuspendHandler.cpp +++ /dev/null @@ -1,637 +0,0 @@ -#include "SuspendHandler.h" - -#include -#include -#include - -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#include -#include - -#pragma comment( lib, "wtsapi32.lib" ) -#endif - -SuspendHandlerBase::SuspendHandlerBase() - : _isSuspendEnabled(false) - , _isLockEnabled(false) - , _isSuspendApiEnabled(false) - , _isIdleApiEnabled(false) - , _isSuspendOnLock(false) - , _isSuspendRegistered(false) - , _isLockRegistered(false) - , _isSuspendApiRegistered(false) - , _isIdleApiRegistered(false) - , _isSuspended(false) - , _isIdle(false) - , _isLocked (false) - -{ - _log = Logger::getInstance("EVENTS"); -} - -SuspendHandlerBase::~SuspendHandlerBase() -{ - unregisterLockHandler(); - unregisterSuspendHandler(); - - unregisterIdleApiHandler(); - unregisterSuspendApiHandler(); -} - -void SuspendHandlerBase::handleSettingsUpdate(settings::type type, const QJsonDocument& config) -{ - if(type == settings::SYSTEMEVENTS) - { - 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 - registerSuspendHandler(); - } - else - { - unregisterSuspendHandler(); - } - - _isLockEnabled = obj["lockEnable"].toBool(true); - if (_isLockEnabled || _isSuspendOnLock != prevIsSuspendOnLock) - { - // Listen to lock/screensaver events received by the OS - registerLockHandler(); - } - else - { - unregisterLockHandler(); - } - - //Handle API event related configurations - _isSuspendApiEnabled = obj["suspendApiEnable"].toBool(true); - if (_isSuspendApiEnabled) - { - // Listen to suspend/resume/idle events received by the OS - registerSuspendApiHandler(); - } - else - { - unregisterSuspendApiHandler(); - } - - _isIdleApiEnabled = obj["idleApiEnable"].toBool(true); - if (_isIdleApiEnabled) - { - // Listen to lock/screensaver events received by the OS - registerIdleApiHandler(); - } - else - { - unregisterIdleApiHandler(); - } - } -} - -bool SuspendHandlerBase::registerSuspendHandler() -{ - if (!_isSuspendRegistered) - { - QObject::connect(this, &SuspendHandlerBase::suspendEvent, HyperionIManager::getInstance(), &HyperionIManager::suspend); - QObject::connect(this, &SuspendHandlerBase::resumeEvent, HyperionIManager::getInstance(), &HyperionIManager::resume); - Info(_log, "Registered for suspend/resume events."); - _isSuspendRegistered = true; - } - return true; -} - -void SuspendHandlerBase::unregisterSuspendHandler() -{ - if (_isSuspendRegistered) - { - QObject::disconnect(this, &SuspendHandlerBase::suspendEvent, HyperionIManager::getInstance(), &HyperionIManager::suspend); - QObject::disconnect(this, &SuspendHandlerBase::resumeEvent, HyperionIManager::getInstance(), &HyperionIManager::resume); - Info(_log, "Unregistered for suspend/resume events."); - _isSuspendRegistered = false; - } -} - -bool SuspendHandlerBase::registerLockHandler() -{ - QObject::disconnect(this, &SuspendHandlerBase::lockedEvent,nullptr, nullptr); - if (_isSuspendOnLock) - { - QObject::connect(this, &SuspendHandlerBase::lockedEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleSuspend); - } - else - { - QObject::connect(this, &SuspendHandlerBase::lockedEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleIdle); - } - QObject::connect(this, &SuspendHandlerBase::idleEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleIdle); - Info(_log, "Registered for lock/unlock events. %s on lock event.", _isSuspendOnLock ? "Suspend" : "Idle"); - _isLockRegistered = true; - return true; -} - -void SuspendHandlerBase::unregisterLockHandler() -{ - if (_isLockRegistered) - { - QObject::disconnect(this, &SuspendHandlerBase::lockedEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleSuspend); - QObject::disconnect(this, &SuspendHandlerBase::lockedEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleIdle); - QObject::disconnect(this, &SuspendHandlerBase::idleEvent, HyperionIManager::getInstance(), &HyperionIManager::toggleIdle); - Info(_log, "Unregistered for lock/unlock events."); - _isLockRegistered = false; - } -} - - -bool SuspendHandlerBase::registerSuspendApiHandler() -{ - if (!_isSuspendApiRegistered) - { - QObject::connect(HyperionIManager::getInstance(), &HyperionIManager::triggerSuspend, this, QOverload::of(&SuspendHandler::suspend)); - QObject::connect(HyperionIManager::getInstance(), &HyperionIManager::triggerToggleSuspend, this, &SuspendHandler::toggleSuspend); - Info(_log, "Registered for suspend/resume API events."); - _isSuspendApiRegistered = true; - } - return true; -} - -void SuspendHandlerBase::unregisterSuspendApiHandler() -{ - if (_isSuspendApiRegistered) - { - QObject::disconnect(HyperionIManager::getInstance(), &HyperionIManager::triggerSuspend, this, QOverload::of(&SuspendHandler::suspend)); - QObject::disconnect(HyperionIManager::getInstance(), &HyperionIManager::triggerToggleSuspend, this, &SuspendHandler::toggleSuspend); - Info(_log, "Unregistered for suspend/resume API events."); - _isSuspendApiRegistered = false; - } -} - -bool SuspendHandlerBase::registerIdleApiHandler() -{ - if (!_isIdleApiRegistered) - { - QObject::connect(HyperionIManager::getInstance(), &HyperionIManager::triggerIdle, this, &SuspendHandler::idle); - QObject::connect(HyperionIManager::getInstance(), &HyperionIManager::triggerToggleIdle, this, &SuspendHandler::toggleIdle); - Info(_log, "Registered for idle API events."); - _isIdleApiRegistered = true; - } - return true; -} - -void SuspendHandlerBase::unregisterIdleApiHandler() -{ - if (_isIdleApiRegistered) - { - QObject::disconnect(HyperionIManager::getInstance(), &HyperionIManager::triggerIdle, this, &SuspendHandler::idle); - QObject::disconnect(HyperionIManager::getInstance(), &HyperionIManager::triggerToggleIdle, this, &SuspendHandler::toggleIdle); - Info(_log, "Unregistered for idle API events."); - _isIdleApiRegistered = false; - } -} - -void SuspendHandlerBase::suspend() -{ - suspend(true); -} - -void SuspendHandlerBase::suspend(bool sleep) -{ - if (sleep) - { - if (!_isSuspended) - { - _isSuspended = true; - Info(_log, "Suspend event received - Hyperion is going to sleep"); - emit suspendEvent(); - } - else - { - Debug(_log, "Suspend event ignored - already suspended"); - } - } - else - { - if (_isSuspended || _isIdle) - { - Info(_log, "Resume event received - Hyperion is going into working mode"); - emit resumeEvent(); - _isSuspended = false; - _isIdle = false; - } - else - { - Debug(_log, "Resume event ignored - not in suspend nor idle mode"); - } - } -} - -void SuspendHandlerBase::resume() -{ - suspend(false); -} - -void SuspendHandlerBase::toggleSuspend() -{ - Debug(_log, "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(_log, "Idle event received"); - emit idleEvent(isIdle); - } - } - else - { - if (_isIdle) - { - Info(_log, "Resume from idle event recevied"); - emit idleEvent(isIdle); - _isIdle = false; - } - } - } - else - { - Debug(_log, "Idle event ignored - Hyperion is suspended"); - } -} - -void SuspendHandlerBase::toggleIdle() -{ - Debug(_log, "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(_log, "Screen lock event received"); - emit lockedEvent(isLocked); - } - } - else - { - if (_isLocked) - { - Info(_log, "Screen unlock event received"); - emit lockedEvent(isLocked); - _isLocked = false; - } - } - } - else - { - Debug(_log, "Screen lock event ignored - Hyperion is suspended"); - } -} - -#if defined(_WIN32) - -SuspendHandlerWindows::SuspendHandlerWindows() - : _notifyHandle(NULL) -{ -} - -SuspendHandlerWindows::~SuspendHandlerWindows() -{ - unregisterLockHandler(); - unregisterSuspendHandler(); -} - -#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(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 SuspendHandlerWindows::registerSuspendHandler() -{ - bool isRegistered{ _isSuspendRegistered }; - if (!_isSuspendRegistered) - { - auto handle = reinterpret_cast (_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) - { - isRegistered = SuspendHandlerBase::registerSuspendHandler(); - } - } - return isRegistered; -} - -void SuspendHandlerWindows::unregisterSuspendHandler() -{ - if (_isSuspendRegistered) - { - if (_notifyHandle != NULL) - { - QCoreApplication::instance()->removeNativeEventFilter(this); - UnregisterSuspendResumeNotification(_notifyHandle); - } - _notifyHandle = NULL; - SuspendHandlerBase::unregisterSuspendHandler(); - } -} - -bool SuspendHandlerWindows::registerLockHandler() -{ - bool isRegistered{ _isLockRegistered }; - if (!_isLockRegistered) - { - auto handle = reinterpret_cast (_widget.winId()); - if (WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION)) - { - isRegistered = true; - } - else - { - Error(_log, "Could not register for lock/unlock events!"); - } - - - } - - if (isRegistered) - { - isRegistered = SuspendHandlerBase::registerLockHandler(); - } - return isRegistered; -} - -void SuspendHandlerWindows::unregisterLockHandler() -{ - if (_isLockRegistered) - { - auto handle = reinterpret_cast (_widget.winId()); - WTSUnRegisterSessionNotification(handle); - SuspendHandlerBase::unregisterLockHandler(); - } -} - -#elif defined(__linux__) && defined(HYPERION_HAS_DBUS) -#include - -struct dBusSignals -{ - QString service; - QString path; - QString interface; - QString name; -}; - -typedef QMultiMap 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() -{ -} - -bool SuspendHandlerLinux::registerSuspendHandler() -{ - 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) - { - isRegistered = SuspendHandlerBase::registerSuspendHandler(); - } - - } - return isRegistered; -} - -void SuspendHandlerLinux::unregisterSuspendHandler() -{ - 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)); - SuspendHandlerBase::unregisterSuspendHandler(); - } - else - { - Error(_log, "Could not unregister for suspend/resume events via service: %s", QSTRING_CSTR(service)); - } - } - } -} - -bool SuspendHandlerLinux::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) - { - isRegistered = SuspendHandlerBase::registerLockHandler(); - } - - return isRegistered; -} - -void SuspendHandlerLinux::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) - { - SuspendHandlerBase::unregisterLockHandler(); - } - } - } -} - - -#endif diff --git a/src/hyperiond/SuspendHandler.h b/src/hyperiond/SuspendHandler.h deleted file mode 100644 index ff8260e8..00000000 --- a/src/hyperiond/SuspendHandler.h +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef SUSPENDHANDLER_H -#define SUSPENDHANDLER_H -#include -#include - -#if defined(_WIN32) -#include -#include -#include -#include -#endif - -#include - -class Logger; - -class SuspendHandlerBase : public QObject { - Q_OBJECT - -public: - SuspendHandlerBase(); - ~SuspendHandlerBase() override; - -public slots: - - void suspend(bool sleep); - - void suspend(); - void resume(); - void toggleSuspend(); - - void idle(bool isIdle); - void toggleIdle(); - - void lock(bool isLocked); - - virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config); - -signals: - - void suspendEvent(); - void resumeEvent(); - void lockedEvent(bool); - void idleEvent(bool); - -protected: - - virtual bool registerSuspendHandler(); - virtual void unregisterSuspendHandler(); - virtual bool registerLockHandler(); - virtual void unregisterLockHandler(); - - virtual bool registerSuspendApiHandler(); - virtual void unregisterSuspendApiHandler(); - virtual bool registerIdleApiHandler(); - virtual void unregisterIdleApiHandler(); - - bool _isSuspendEnabled; - bool _isLockEnabled; - bool _isSuspendApiEnabled; - bool _isIdleApiEnabled; - bool _isSuspendOnLock; - - bool _isSuspendRegistered; - bool _isLockRegistered; - bool _isSuspendApiRegistered; - bool _isIdleApiRegistered; - - Logger * _log {}; - -private: - - bool _isSuspended; - bool _isIdle; - bool _isLocked; -}; - -#if defined(_WIN32) - -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: - - bool registerSuspendHandler() override; - void unregisterSuspendHandler() override; - bool registerLockHandler() override; - void unregisterLockHandler() override; - - QWidget _widget; - HPOWERNOTIFY _notifyHandle; -}; - -using SuspendHandler = SuspendHandlerWindows; - -#elif defined(__linux__) && defined(HYPERION_HAS_DBUS) - -class SuspendHandlerLinux : public SuspendHandlerBase { - Q_OBJECT - -public: - SuspendHandlerLinux(); - -private: - bool registerSuspendHandler() override; - void unregisterSuspendHandler() override; - bool registerLockHandler() override; - void unregisterLockHandler() override; -}; - -using SuspendHandler = SuspendHandlerLinux; - -#else -using SuspendHandler = SuspendHandlerBase; -#endif - -#endif // SUSPENDHANDLER_H diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index b34de01d..469a7245 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -97,7 +97,8 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo #ifdef ENABLE_CEC , _cecHandler(nullptr) #endif - , _suspendHandler(nullptr) + , _eventHandler(nullptr) + , _osEventHandler(nullptr) , _currVideoMode(VideoMode::VIDEO_2D) { HyperionDaemon::daemon = this; @@ -119,8 +120,6 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo handleSettingsUpdate(settings::LOGGER, getSetting(settings::LOGGER)); } - createCecHandler(); - //Create MdnsBrowser singleton in main tread to ensure thread affinity during destruction #ifdef ENABLE_MDNS MdnsBrowser::getInstance(); @@ -178,7 +177,7 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo startNetworkServices(); // init events services - handleSettingsUpdate(settings::SYSTEMEVENTS, getSetting(settings::SYSTEMEVENTS)); + handleSettingsUpdate(settings::OSEVENTS, getSetting(settings::OSEVENTS)); } HyperionDaemon::~HyperionDaemon() @@ -234,7 +233,10 @@ void HyperionDaemon::handleInstanceStateChange(InstanceState state, quint8 insta connect(_sslWebserver, &WebServer::publishService, _mDNSProvider, &MdnsProvider::publishService); #endif sslWsThread->start(); + + //startCecHandler(); } + break; case InstanceState::H_STOPPED: @@ -263,6 +265,8 @@ void HyperionDaemon::freeObjects() { Debug(_log, "Cleaning up Hyperion before quit."); + stopCecHandler(); + #ifdef ENABLE_MDNS if (_mDNSProvider != nullptr) { @@ -333,19 +337,7 @@ void HyperionDaemon::freeObjects() _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; + delete _osEventHandler; // stop Hyperions (non blocking) _instanceManager->stopAll(); @@ -693,19 +685,6 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs } 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 (_videoGrabber == nullptr) { @@ -743,18 +722,28 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs Debug(_log, "Audio capture not supported on this platform"); #endif } - else if (settingsType == settings::SYSTEMEVENTS) + else if (settingsType == settings::OSEVENTS) { - // Create suspend handler - if (_suspendHandler == nullptr) + if (_eventHandler == nullptr) { - _suspendHandler = new SuspendHandler(); - _suspendHandler->handleSettingsUpdate(settings::SYSTEMEVENTS, getSetting(settings::SYSTEMEVENTS)); - - connect(this, &HyperionDaemon::settingsChanged, _suspendHandler, &SuspendHandler::handleSettingsUpdate); - - Debug(_log, "SuspendHandler created"); + _eventHandler = EventHandler::getInstance(); + Debug(_log, "Hyperion event handler created"); } + + // Create suspend handler + 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"); + } + } + else if (settingsType == settings::CECEVENTS) + { + startCecHandler(); } } @@ -929,26 +918,47 @@ void HyperionDaemon::createGrabberOsx(const QJsonObject& grabberConfig) #endif } -void HyperionDaemon::createCecHandler() +void HyperionDaemon::startCecHandler() { -#if defined(ENABLE_V4L2) && defined(ENABLE_CEC) - _cecHandler = new CECHandler; +#if defined(ENABLE_CEC) + if (_cecHandler == nullptr) + { + _cecHandler = new CECHandler; - QThread* thread = new QThread(this); - thread->setObjectName("CECThread"); - _cecHandler->moveToThread(thread); - thread->start(); + QThread* cecHandlerThread = new QThread(this); + cecHandlerThread->setObjectName("CECThread"); + _cecHandler->moveToThread(cecHandlerThread); - connect(_cecHandler, &CECHandler::cecEvent, [&](CECEvent event) { - if (_videoGrabber != nullptr) - { - _videoGrabber->handleCecEvent(event); - } - }); + connect(cecHandlerThread, &QThread::started, _cecHandler, &CECHandler::start); + connect(cecHandlerThread, &QThread::finished, _cecHandler, &CECHandler::stop); - Info(_log, "CEC handler created"); + cecHandlerThread->start(); + + _cecHandler->handleSettingsUpdate(settings::CECEVENTS, getSetting(settings::CECEVENTS)); + connect(this, &HyperionDaemon::settingsChanged, _cecHandler, &CECHandler::handleSettingsUpdate); + + Info(_log, "CEC event handler created"); + } #else Debug(_log, "The CEC handler is not supported on this platform"); #endif } +void HyperionDaemon::stopCecHandler() +{ +#if defined(ENABLE_CEC) + if (_cecHandler != nullptr) + { + disconnect(_cecHandler, &CECHandler::signalEvent, _eventHandler, &EventHandler::handleEvent); + + auto cecHandlerThread = _cecHandler->thread(); + cecHandlerThread->quit(); + cecHandlerThread->wait(); + delete cecHandlerThread; + delete _cecHandler; + _cecHandler = nullptr; + } + Info(_log, "CEC handler stopped"); +#endif +} + diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index 2a292746..2a4a8c9e 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -75,7 +75,8 @@ #include #include -#include "SuspendHandler.h" +#include +#include class HyperionIManager; class SysTray; @@ -103,18 +104,13 @@ class HyperionDaemon : public QObject public: HyperionDaemon(const QString& rootPath, QObject *parent, bool logLvlOverwrite, bool readonlyMode = false); - ~HyperionDaemon(); + ~HyperionDaemon() override; /// /// @brief Get webserver pointer (systray) /// WebServer *getWebServerInstance() { return _webserver; } - /// - /// @brief Get suspense handler pointer - /// - SuspendHandler* getSuspendHandlerInstance() { return _suspendHandler; } - /// /// @brief Get the current videoMode /// @@ -185,10 +181,12 @@ private: void createGrabberX11(const QJsonObject & grabberConfig); void createGrabberXcb(const QJsonObject & grabberConfig); void createGrabberQt(const QJsonObject & grabberConfig); - void createCecHandler(); void createGrabberDx(const QJsonObject & grabberConfig); void createGrabberAudio(const QJsonObject & grabberConfig); + void startCecHandler(); + void stopCecHandler(); + Logger* _log; HyperionIManager* _instanceManager; AuthManager* _authManager; @@ -216,7 +214,8 @@ private: #ifdef ENABLE_CEC CECHandler* _cecHandler; #endif - SuspendHandler* _suspendHandler; + EventHandler* _eventHandler; + OsEventHandler* _osEventHandler; #if defined(ENABLE_FLATBUF_SERVER) FlatBufferServer* _flatBufferServer; diff --git a/src/hyperiond/main.cpp b/src/hyperiond/main.cpp index fa9352b9..b20c9ab2 100644 --- a/src/hyperiond/main.cpp +++ b/src/hyperiond/main.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -46,40 +45,12 @@ #include "hyperiond.h" #include "systray.h" -#include "SuspendHandler.h" +#include using namespace commandline; #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[]) { bool isGuiApp = false; @@ -150,11 +121,6 @@ int main(int argc, char** argv) DefaultSignalHandler::install(); -#ifndef _WIN32 - signal(SIGCHLD, signal_handler); - signal(SIGUSR1, signal_handler); - signal(SIGUSR2, signal_handler); -#endif // force the locale setlocale(LC_ALL, "C"); QLocale::setDefault(QLocale::c()); diff --git a/src/hyperiond/systray.cpp b/src/hyperiond/systray.cpp index ab7b6c8a..0d715652 100644 --- a/src/hyperiond/systray.cpp +++ b/src/hyperiond/systray.cpp @@ -22,10 +22,11 @@ #endif #include #include +#include #include "hyperiond.h" #include "systray.h" -#include "SuspendHandler.h" + SysTray::SysTray(HyperionDaemon *hyperiond) : QWidget() @@ -33,7 +34,6 @@ SysTray::SysTray(HyperionDaemon *hyperiond) , _hyperiond(hyperiond) , _hyperion(nullptr) , _instanceManager(HyperionIManager::getInstance()) - , _suspendHandler (hyperiond->getSuspendHandlerInstance()) , _webPort(8090) { Q_INIT_RESOURCE(resources); @@ -84,13 +84,16 @@ void SysTray::createTrayIcon() restartAction->setIcon(QPixmap(":/restart.svg")); connect(restartAction, &QAction::triggered, this , [=](){ Process::restartHyperion(12); }); + + // TODO: Check if can be done with SystemEvents suspendAction = new QAction(tr("&Suspend"), this); suspendAction->setIcon(QPixmap(":/suspend.svg")); - connect(suspendAction, &QAction::triggered, _suspendHandler, QOverload<>::of(&SuspendHandler::suspend)); + connect(suspendAction, &QAction::triggered, EventHandler::getInstance(), QOverload<>::of(&EventHandler::suspend)); resumeAction = new QAction(tr("&Resume"), this); resumeAction->setIcon(QPixmap(":/resume.svg")); - connect(resumeAction, &QAction::triggered, _suspendHandler, &SuspendHandler::resume); + + connect(resumeAction, &QAction::triggered, EventHandler::getInstance(), &EventHandler::resume); colorAction = new QAction(tr("&Color"), this); colorAction->setIcon(QPixmap(":/color.svg")); diff --git a/src/hyperiond/systray.h b/src/hyperiond/systray.h index 8f28fc29..bab57bf0 100644 --- a/src/hyperiond/systray.h +++ b/src/hyperiond/systray.h @@ -12,7 +12,7 @@ #include #include -#include "SuspendHandler.h" +#include class HyperionDaemon; @@ -22,13 +22,13 @@ class SysTray : public QWidget public: SysTray(HyperionDaemon *hyperiond); - ~SysTray(); + ~SysTray() override; public slots: void showColorDialog(); void setColor(const QColor & color); - void closeEvent(QCloseEvent *event); + void closeEvent(QCloseEvent *event) override; void settings() const; #if defined(ENABLE_EFFECTENGINE) void setEffect(); @@ -42,7 +42,7 @@ private slots: /// /// @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 @@ -84,6 +84,4 @@ private: Hyperion *_hyperion; HyperionIManager *_instanceManager; quint16 _webPort; - - SuspendHandler *_suspendHandler; };