diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index f88532d5..b01d1cc0 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -192,6 +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_cec_events_heading_title": "CEC Events", + "conf_cec_events_intro": "Settings related to diffent CEC (Consumer Electronics Control) protocol events Hyperion can handle", "conf_os_events_heading_title": "Operating System Events", "conf_os_events_intro": "Settings related to diffent Operating System events Hyperion can handle", "conf_webconfig_label_intro": "Webconfiguration settings. Edit wisely.", @@ -245,6 +247,8 @@ "edt_append_pixel": "Pixel", "edt_append_s": "s", "edt_append_sdegree": "s/degree", + "edt_conf_action_title": "Action", + "edt_conf_action_expl": "Action to be applied", "edt_conf_bb_blurRemoveCnt_expl": "Number of pixels that get removed from the detected border to cut away blur.", "edt_conf_bb_blurRemoveCnt_title": "Blur pixel", "edt_conf_bb_borderFrameCnt_expl": "Number of frames before a consistent detected border is set.", @@ -260,6 +264,19 @@ "edt_conf_bb_unknownFrameCnt_title": "Unknown frames", "edt_conf_bge_heading_title": "Background Effect/Color", "edt_conf_bobls_heading_title": "Boblight Server", + "edt_conf_cec_actions_header_title": "Actions", + "edt_conf_cec_actions_header_expl": "Define which action should take place on a recognised CEC event", + "edt_conf_cec_actions_header_item_title": "Action", + "edt_conf_cec_button_release_delay_ms_title": "Button release time", + "edt_conf_cec_button_release_delay_ms_expl": "Remote button press release time", + "edt_conf_cec_button_repeat_rate_ms_title": "Button repeat rate", + "edt_conf_cec_button_repeat_rate_ms_expl": "Remote button press repeat rate", + "edt_conf_cec_double_tap_timeout_ms_title": "Button delay before repeating", + "edt_conf_cec_double_tap_timeout_ms_expl": "Remote button press delay before repeating", + "edt_conf_cec_event_title": "CEC Event", + "edt_conf_cec_event_expl": "CEC event that will trigger an action", + "edt_conf_cec_events_heading_title": "Events", + "edt_conf_cec_events_heading_expl": "Events explain", "edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.", "edt_conf_color_accuracyLevel_title": "Accuracy level", "edt_conf_color_backlightColored_expl": "Add some color to your backlight.", @@ -322,6 +339,13 @@ "edt_conf_enum_HORIZONTAL": "Horizontal", "edt_conf_enum_VERTICAL": "Vertical", "edt_conf_enum_BOTH": "Horizontal & Vertical", + "edt_conf_enum_action_idle": "Idle", + "edt_conf_enum_action_restart": "Restart", + "edt_conf_enum_action_resume": "Resume", + "edt_conf_enum_action_resumeIdle": "ResumeIdle", + "edt_conf_enum_action_suspend": "Suspend", + "edt_conf_enum_action_toggleIdle": "ToggleIdle", + "edt_conf_enum_action_toggleSuspend": "ToggleSuspend", "edt_conf_enum_automatic": "Automatic", "edt_conf_enum_bbclassic": "Classic", "edt_conf_enum_bbdefault": "Default", @@ -330,6 +354,12 @@ "edt_conf_enum_bgr": "BGR", "edt_conf_enum_bottom_up": "Bottom up", "edt_conf_enum_brg": "BRG", + "edt_conf_enum_cec_key_f1_blue": "Blue button pressed", + "edt_conf_enum_cec_key_f2_red": "Red button pressed", + "edt_conf_enum_cec_key_f3_green": "Green button pressed", + "edt_conf_enum_cec_key_f4_yellow": "Yellow button pressed", + "edt_conf_enum_cec_opcode_set stream path": "TV on", + "edt_conf_enum_cec_opcode_standby": "TV off", "edt_conf_enum_color": "Color", "edt_conf_enum_custom": "Custom", "edt_conf_enum_decay": "Decay", diff --git a/assets/webconfig/js/content_events.js b/assets/webconfig/js/content_events.js index 92ed4d90..e4097fc0 100644 --- a/assets/webconfig/js/content_events.js +++ b/assets/webconfig/js/content_events.js @@ -1,11 +1,10 @@ $(document).ready(function () { performTranslation(); - var CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1); - - var conf_editor_osEvents = null; - var conf_editor_cecEvents = null; + const CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1); + let conf_editor_osEvents = null; + let conf_editor_cecEvents = null; if (window.showOptHelp) { //Operating System Events @@ -48,7 +47,9 @@ $(document).ready(function () { }, true, true); conf_editor_cecEvents.on('change', function () { - var cecEventsEnable = conf_editor_cecEvents.getEditor("root.cecEvents.enable").getValue(); + + const cecEventsEnable = conf_editor_cecEvents.getEditor("root.cecEvents.enable").getValue(); + if (cecEventsEnable) { showInputOptionsForKey(conf_editor_cecEvents, "cecEvents", "enable", true); $('#cecEventsHelpPanelId').show(); @@ -56,11 +57,16 @@ $(document).ready(function () { 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()); + + const saveOptions = conf_editor_cecEvents.getValue(); + // Workaround, as otherwise actions array is empty + saveOptions.cecEvents.actions = conf_editor_cecEvents.getEditor("root.cecEvents.actions").getValue(); + requestWriteConfig(saveOptions); }); } @@ -71,7 +77,7 @@ $(document).ready(function () { createHint("intro", $.i18n('conf_cec_events_intro'), "editor_container_cec_events"); } } - + removeOverlay(); }); diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index 6136673a..91c5ecce 100755 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -5,7 +5,6 @@ $(document).ready(function () { var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0); var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0); const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0); - var CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1); var conf_editor_video = null; var conf_editor_audio = null; @@ -369,11 +368,6 @@ $(document).ready(function () { conf_editor_video.on('change', function () { - // Hide elements not supported by the backend - if (window.serverInfo.cec.enabled === false || !CEC_ENABLED) { - showInputOptionForItem(conf_editor_video, "grabberV4L2", "cecDetection", false); - } - // Validate the current editor's content if (!conf_editor_video.validate().length) { var deviceSelected = conf_editor_video.getEditor("root.grabberV4L2.available_devices").getValue(); diff --git a/include/cec/CECHandler.h b/include/cec/CECHandler.h index 38f12036..6f2b7c5d 100644 --- a/include/cec/CECHandler.h +++ b/include/cec/CECHandler.h @@ -2,14 +2,13 @@ #include #include +#include #include #include #include - -//#include #include using CECCallbacks = CEC::ICECCallbacks; @@ -33,23 +32,28 @@ class CECHandler : public QObject { Q_OBJECT public: - CECHandler(); + CECHandler(const QJsonDocument& config, QObject * parent = nullptr); ~CECHandler() override; QString scan() const; public slots: + bool start(); void stop(); virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config); -signals: - //void cecEvent(CECEvent event); +private: +signals: void signalEvent(Event event); private: + + bool enable(); + void disable(); + /* CEC Callbacks */ static void onCecLogMessage (void * context, const CECLogMessage * message); static void onCecKeyPress (void * context, const CECKeyPress * key); @@ -64,6 +68,11 @@ private: bool openAdapter(const CECAdapterDescriptor & descriptor); void printAdapter(const CECAdapterDescriptor & descriptor) const; + // CEC Event Strings per https://github.com/Pulse-Eight/libcec/blob/master/src/libcec/CECTypeUtils.h + void triggerAction(const QString& cecEvent); + + QJsonDocument _config; + /* CEC Helpers */ CECCallbacks getCallbacks() const; CECConfig getConfig() const; @@ -72,7 +81,14 @@ private: CECCallbacks _cecCallbacks {}; CECConfig _cecConfig {}; + bool _isInitialised; bool _isEnabled; + int _buttonReleaseDelayMs; + int _buttonRepeatRateMs; + int _doubleTapTimeoutMs; + + QMap _cecEventActionMap; + Logger * _logger {}; }; diff --git a/include/events/Event.h b/include/events/Event.h index 18885557..9a0ea39b 100644 --- a/include/events/Event.h +++ b/include/events/Event.h @@ -1,8 +1,11 @@ #ifndef EVENT_H #define EVENT_H +#include + enum class Event { + Unknown, Suspend, Resume, ToggleSuspend, @@ -19,14 +22,29 @@ inline const char* eventToString(Event event) { case Event::Suspend: return "Suspend"; case Event::Resume: return "Resume"; - case Event::ToggleSuspend: return "ToggleSuspend detector"; + case Event::ToggleSuspend: return "ToggleSuspend"; case Event::Idle: return "Idle"; case Event::ResumeIdle: return "ResumeIdle"; case Event::ToggleIdle: return "ToggleIdle"; case Event::Reload: return "Reload"; case Event::Restart: return "Restart"; + case Event::Unknown: default: return "Unknown"; } } +inline Event stringToEvent(const QString& event) +{ + if (event.compare("Suspend")==0) return Event::Suspend; + if (event.compare("Resume")==0) return Event::Resume; + if (event.compare("ToggleSuspend")==0) return Event::ToggleSuspend; + if (event.compare("Idle")==0) return Event::Idle; + if (event.compare("ResumeIdle")==0) return Event::ResumeIdle; + if (event.compare("ToggleIdle")==0) return Event::ToggleIdle; + if (event.compare("Reload")==0) return Event::Reload; + if (event.compare("Restart")==0) return Event::Restart; + return Event::Unknown; +} + + #endif // EVENT_H diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 8cc62761..9b787262 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -113,7 +113,7 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject Q_INIT_RESOURCE(JSONRPC_schemas); - qRegisterMetaType("Event"); + qRegisterMetaType("Event"); } void JsonAPI::initialize() diff --git a/libsrc/cec/CECHandler.cpp b/libsrc/cec/CECHandler.cpp index 5f6c8504..f019ac2e 100644 --- a/libsrc/cec/CECHandler.cpp +++ b/libsrc/cec/CECHandler.cpp @@ -13,10 +13,17 @@ #include /* Enable to turn on detailed CEC logs */ -#define VERBOSE_CEC +#define NOVERBOSE_CEC -CECHandler::CECHandler() - : _isEnabled(false) +CECHandler::CECHandler(const QJsonDocument& config, QObject * parent) + : QObject(parent) + , _config(config) + , _isInitialised(false) + , _isEnabled(false) + , _buttonReleaseDelayMs(CEC_BUTTON_TIMEOUT) + , _buttonRepeatRateMs(0) + , _doubleTapTimeoutMs(CEC_DOUBLE_TAP_TIMEOUT_MS) + , _cecEventActionMap() { qRegisterMetaType("Event"); @@ -30,79 +37,149 @@ CECHandler::CECHandler() CECHandler::~CECHandler() { - stop(); } void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { if(type == settings::CECEVENTS) { + Debug( _logger, "config: [%s]", QString(QJsonDocument(config).toJson(QJsonDocument::Compact)).toUtf8().constData()); + const QJsonObject& obj = config.object(); - _isEnabled = obj["Enable"].toBool(false); + _isEnabled = obj["enable"].toBool(false); + Debug(_logger, "CEC Event handling is %s", _isEnabled? "enabled" : "disabled"); - Debug(_logger, "_isEnabled: [%d]", _isEnabled); + _buttonReleaseDelayMs = obj["buttonReleaseDelayMs"].toInt(CEC_BUTTON_TIMEOUT); + _buttonRepeatRateMs = obj["buttonRepeatRateMs"].toInt(0); + _doubleTapTimeoutMs = obj["doubleTapTimeoutMs"].toInt(CEC_DOUBLE_TAP_TIMEOUT_MS); + _cecConfig.iButtonReleaseDelayMs = static_cast(_buttonReleaseDelayMs); + _cecConfig.iButtonRepeatRateMs = static_cast(_buttonRepeatRateMs); + _cecConfig.iDoubleTapTimeoutMs = static_cast(_doubleTapTimeoutMs); + if (_cecAdapter->SetConfiguration(&_cecConfig)) + { + Debug(_logger, "Remote button press release time : %dms",_buttonReleaseDelayMs); + Debug(_logger, "Remote button press repeat rate : %dms",_buttonRepeatRateMs); + Debug(_logger, "Remote button press delay before repeating : %dms",_doubleTapTimeoutMs); + } + else + { + Error(_logger, "Failed setting remote button press timing parameters"); + } + + _cecEventActionMap.clear(); + const QJsonArray actionItems = obj["actions"].toArray(); + if (!actionItems.isEmpty()) + { + for (const QJsonValue &item : actionItems) + { + QString cecEvent = item.toObject().value("cec_event").toString(); + QString action = item.toObject().value("action").toString(); + _cecEventActionMap.insert(cecEvent, stringToEvent(action)); + Debug(_logger, "CEC-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(cecEvent), QSTRING_CSTR(action)); + } + } + + if (_isInitialised) + { + if (_isEnabled) + { + if (!_cecEventActionMap.isEmpty()) + { + enable(); + } + else + { + Warning(_logger, "No CEC events to listen to are configured currently."); + } + } + else + { + disable(); + } + } } } bool CECHandler::start() { - if (_cecAdapter) + _isInitialised = false; + if (_cecAdapter == nullptr) { - return true; - } + // std::string library = std::string("" CEC_LIBRARY); + // _cecAdapter = LibCecInitialise(&_cecConfig, QFile::exists(QString::fromStdString(library)) ? library.c_str() : nullptr); - // 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"); - - const auto adapters = getAdapters(); - if (adapters.isEmpty()) - { - Error(_logger, "Failed to find any CEC adapter."); - UnloadLibCec(_cecAdapter); - _cecAdapter = nullptr; - - return false; - } - - Info(_logger, "Auto detecting CEC adapter"); - bool opened = false; - for (const auto & adapter : adapters) - { - printAdapter(adapter); - - if (!opened && openAdapter(adapter)) + _cecAdapter = LibCecInitialise(&_cecConfig); + if(_cecAdapter == nullptr) { - 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; + Error(_logger, "Failed loading libCEC library. CEC is not supported."); + } + else + { + _isInitialised = true; } - } - scan(); + handleSettingsUpdate(settings::CECEVENTS,_config); + } + return _isInitialised; +} + +void CECHandler::stop() +{ + if (_cecAdapter != nullptr) + { + Info(_logger, "Stopping CEC handler"); + _cecAdapter->Close(); + UnloadLibCec(_cecAdapter); + } +} + +bool CECHandler::enable() +{ + bool opened {false}; + if (_isInitialised) + { + const auto adapters = getAdapters(); + if (adapters.isEmpty()) + { + Error(_logger, "Failed to find any CEC adapter."); + _cecAdapter->Close(); + return false; + } + + Info(_logger, "Auto detecting CEC adapter"); + for (const auto & adapter : adapters) + { + printAdapter(adapter); + + if (!opened && openAdapter(adapter)) + { + Info(_logger, "CEC adapter '%s', type: %s initialized." , adapter.strComName, _cecAdapter->ToString(adapter.adapterType)); + opened = true; + break; + } + } + +#ifdef VERBOSE_CEC + std::cout << "Found Devices: " << scan().toStdString() << std::endl; +#endif + } if (!opened) { Error(_logger, "Could not initialize any CEC adapter."); - UnloadLibCec(_cecAdapter); - _cecAdapter = nullptr; + _cecAdapter->Close(); + } + else + { + QObject::connect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); + Info(_logger, "CEC handler started"); } return opened; } -void CECHandler::stop() +void CECHandler::disable() { if (_cecAdapter != nullptr) { @@ -111,8 +188,6 @@ void CECHandler::stop() QObject::disconnect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); _cecAdapter->Close(); - UnloadLibCec(_cecAdapter); - _cecAdapter = nullptr; } } @@ -146,12 +221,11 @@ CECCallbacks CECHandler::getCallbacks() const QVector CECHandler::getAdapters() const { - if (!_cecAdapter) + if (_cecAdapter == nullptr) return {}; QVector descriptors(16); - //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*/); + int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), static_cast(descriptors.size()), nullptr, true /*quickscan*/); descriptors.resize(size); return descriptors; @@ -159,7 +233,7 @@ QVector CECHandler::getAdapters() const bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor) { - if (!_cecAdapter) + if (_cecAdapter == nullptr) return false; if(!_cecAdapter->Open(descriptor.strComName)) @@ -186,7 +260,7 @@ void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const QString CECHandler::scan() const { - if (!_cecAdapter) + if (_cecAdapter == nullptr) return {}; Info(_logger, "Starting CEC scan"); @@ -219,17 +293,24 @@ QString CECHandler::scan() const ); } } - - std::cout << "Devices: " << QJsonDocument(devices).toJson().toStdString() << std::endl; - return QJsonDocument(devices).toJson(QJsonDocument::Compact); } +void CECHandler::triggerAction(const QString& cecEvent) +{ + Event action = _cecEventActionMap.value(cecEvent, Event::Unknown); + Debug(_logger, "CEC-Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(cecEvent), eventToString(action) ); + if ( action != Event::Unknown ) + { + emit signalEvent(action); + } +} + void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message) { #ifdef VERBOSE_CEC CECHandler * handler = qobject_cast(static_cast(context)); - if (!handler) + if (handler == nullptr) return; switch (message->level) @@ -256,7 +337,7 @@ void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message) void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key) { CECHandler * handler = qobject_cast(static_cast(context)); - if (!handler) + if (handler == nullptr) return; CECAdapter * adapter = handler->_cecAdapter; @@ -264,20 +345,12 @@ void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key) #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); + handler->triggerAction(adapter->ToString(key->keycode)); break; default: break; @@ -288,7 +361,7 @@ void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParam { #ifdef VERBOSE_CEC CECHandler * handler = qobject_cast(static_cast(context)); - if (!handler) + if (handler == nullptr) return; Error(handler->_logger, QSTRING_CSTR(QString("CECHandler::onCecAlert: %1") @@ -300,7 +373,7 @@ void CECHandler::onCecConfigurationChanged(void * context, const CECConfig * con { #ifdef VERBOSE_CEC CECHandler * handler = qobject_cast(static_cast(context)); - if (!handler) + if (handler == nullptr) return; Debug(handler->_logger, "CECHandler::onCecConfigurationChanged: %s", configuration->strDeviceName); @@ -341,13 +414,10 @@ void CECHandler::onCecCommandReceived(void * context, const CECCommand * command { 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); + { + handler->triggerAction(adapter->ToString(command->opcode)); + } break; default: @@ -363,10 +433,8 @@ void CECHandler::onCecSourceActivated(void * context, const CECLogicalAddress ad #ifdef VERBOSE_CEC CECHandler * handler = qobject_cast(static_cast(context)); - if (!handler) - { + if (handler == nullptr) return; - } CECAdapter * adapter = handler->_cecAdapter; Debug(handler->_logger, QSTRING_CSTR(QString("CEC source %1 : %2") diff --git a/libsrc/cec/CMakeLists.txt b/libsrc/cec/CMakeLists.txt index 5c91eca3..87ae91e0 100644 --- a/libsrc/cec/CMakeLists.txt +++ b/libsrc/cec/CMakeLists.txt @@ -8,8 +8,6 @@ FILE (GLOB CEC_SOURCES "${CURRENT_HEADER_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.h" 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 index 93bf17de..9d239db4 100644 --- a/libsrc/events/CMakeLists.txt +++ b/libsrc/events/CMakeLists.txt @@ -25,6 +25,7 @@ target_include_directories(events PUBLIC ) target_link_libraries(events + hyperion-utils Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets ) diff --git a/libsrc/events/EventHandler.cpp b/libsrc/events/EventHandler.cpp index d980db04..e2181c88 100644 --- a/libsrc/events/EventHandler.cpp +++ b/libsrc/events/EventHandler.cpp @@ -145,7 +145,17 @@ void EventHandler::toggleIdle() void EventHandler::handleEvent(Event event) { - Debug(_log,"%s Event [%d] received", eventToString(event), event); + QObject *senderObj = QObject::sender(); + QString senderObjectClass; + if (senderObj) + { + senderObjectClass = senderObj->metaObject()->className(); + } else + { + senderObjectClass = "unknown sender"; + } + Debug(_log,"%s Event [%d] received from %s", eventToString(event), event, QSTRING_CSTR(senderObjectClass)); + switch (event) { case Event::Suspend: suspend(); @@ -156,7 +166,7 @@ void EventHandler::handleEvent(Event event) break; case Event::ToggleSuspend: - suspend(); + toggleSuspend(); break; case Event::Idle: diff --git a/libsrc/grabber/video/CMakeLists.txt b/libsrc/grabber/video/CMakeLists.txt index 43a0e580..88cc77ad 100644 --- a/libsrc/grabber/video/CMakeLists.txt +++ b/libsrc/grabber/video/CMakeLists.txt @@ -26,7 +26,12 @@ elseif(ENABLE_V4L2) endif() add_library(${PROJECT_NAME} ${SOURCES}) -target_link_libraries(${PROJECT_NAME} hyperion ${QT_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} + hyperion + events + hyperion-utils + ${QT_LIBRARIES} +) if(TURBOJPEG_FOUND) target_link_libraries(${PROJECT_NAME} ${TurboJPEG_LIBRARY}) diff --git a/libsrc/hyperion/CMakeLists.txt b/libsrc/hyperion/CMakeLists.txt index 3a880a91..b0b7bc91 100644 --- a/libsrc/hyperion/CMakeLists.txt +++ b/libsrc/hyperion/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(hyperion target_link_libraries(hyperion blackborder + events hyperion-utils leddevice database diff --git a/libsrc/hyperion/schema/schema-cecEvents.json b/libsrc/hyperion/schema/schema-cecEvents.json index 26c69dc4..2cf29076 100644 --- a/libsrc/hyperion/schema/schema-cecEvents.json +++ b/libsrc/hyperion/schema/schema-cecEvents.json @@ -1,17 +1,119 @@ { - "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 + "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 + }, + "buttonReleaseDelayMs": { + "type": "integer", + "format": "stepper", + "title": "edt_conf_cec_button_release_delay_ms_title", + "append": "edt_append_ms", + "minimum": 0, + "maximum": 500, + "step": 50, + "default": 0, + "required": false, + "access": "advanced", + "propertyOrder": 2 + }, + "buttonRepeatRateMs": { + "type": "integer", + "format": "stepper", + "title": "edt_conf_cec_button_repeat_rate_ms_title", + "append": "edt_append_ms", + "minimum": 0, + "maximum": 250, + "step": 10, + "default": 0, + "required": false, + "access": "advanced", + "propertyOrder": 3 + }, + "doubleTapTimeoutMs": { + "type": "integer", + "format": "stepper", + "title": "edt_conf_cec_double_tap_timeout_ms_title", + "append": "edt_append_ms", + "minimum": 50, + "maximum": 1000, + "step": 50, + "default": 200, + "required": false, + "access": "advanced", + "propertyOrder": 4 + }, + "actions": { + "type": "array", + "title": "edt_conf_cec_actions_header_title", + "minItems": 0, + "uniqueItems": true, + "required": false, + "propertyOrder": 5, + "items": { + "type": "object", + "required": true, + "title": "edt_conf_cec_actions_header_item_title", + "properties": { + "cec_event": { + "type": "string", + "title": "edt_conf_cec_event_title", + "enum": [ + "standby", + "set stream path", + "F1(blue)", + "F2 (red)", + "F3 (green)", + "F4 (yellow)" + ], + "options": { + "enum_titles": [ + "edt_conf_enum_cec_opcode_standby", + "edt_conf_enum_cec_opcode_set stream path", + "edt_conf_enum_cec_key_f1_blue", + "edt_conf_enum_cec_key_f2_red", + "edt_conf_enum_cec_key_f3_green", + "edt_conf_enum_cec_key_f4_yellow" + ] + }, + "propertyOrder": 1 + }, + "action": { + "type": "string", + "title": "edt_conf_action_title", + "enum": [ + "Suspend", + "Resume", + "ToggleSuspend", + "Idle", + "ResumeIdle", + "ToggleIdle", + "Restart" + ], + "options": { + "enum_titles": [ + "edt_conf_enum_action_suspend", + "edt_conf_enum_action_resume", + "edt_conf_enum_action_toggleSuspend", + "edt_conf_enum_action_idle", + "edt_conf_enum_action_resumeIdle", + "edt_conf_enum_action_toggleIdle", + "edt_conf_enum_action_restart" + ] + }, + "propertyOrder": 2 + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false } + diff --git a/libsrc/hyperion/schema/schema-grabberV4L2.json b/libsrc/hyperion/schema/schema-grabberV4L2.json index 1651cb67..396f8868 100644 --- a/libsrc/hyperion/schema/schema-grabberV4L2.json +++ b/libsrc/hyperion/schema/schema-grabberV4L2.json @@ -212,21 +212,13 @@ "required": true, "propertyOrder": 23 }, - "cecDetection": { - "type": "boolean", - "title": "edt_conf_v4l2_cecDetection_title", - "default": false, - "required": true, - "access": "advanced", - "propertyOrder": 24 - }, "signalDetection": { "type": "boolean", "title": "edt_conf_v4l2_signalDetection_title", "default": false, "required": true, "access": "expert", - "propertyOrder": 25 + "propertyOrder": 24 }, "redSignalThreshold": { "type": "integer", @@ -242,7 +234,7 @@ }, "access": "expert", "required": true, - "propertyOrder": 26 + "propertyOrder": 25 }, "greenSignalThreshold": { "type": "integer", @@ -258,7 +250,7 @@ }, "required": true, "access": "expert", - "propertyOrder": 27 + "propertyOrder": 26 }, "blueSignalThreshold": { "type": "integer", @@ -274,7 +266,7 @@ }, "required": true, "access": "expert", - "propertyOrder": 28 + "propertyOrder": 27 }, "noSignalCounterThreshold": { "type": "integer", @@ -289,7 +281,7 @@ }, "required": true, "access": "expert", - "propertyOrder": 29 + "propertyOrder": 28 }, "sDVOffsetMin": { "type": "number", @@ -305,7 +297,7 @@ }, "required": true, "access": "expert", - "propertyOrder": 30 + "propertyOrder": 29 }, "sDVOffsetMax": { "type": "number", @@ -321,7 +313,7 @@ }, "required": true, "access": "expert", - "propertyOrder": 31 + "propertyOrder": 30 }, "sDHOffsetMin": { "type": "number", @@ -337,7 +329,7 @@ }, "required": true, "access": "expert", - "propertyOrder": 32 + "propertyOrder": 31 }, "sDHOffsetMax": { "type": "number", @@ -353,7 +345,7 @@ }, "required": true, "access": "expert", - "propertyOrder": 33 + "propertyOrder": 32 } }, "additionalProperties": true diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 469a7245..fa0a7849 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -178,6 +178,7 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo // init events services handleSettingsUpdate(settings::OSEVENTS, getSetting(settings::OSEVENTS)); + handleSettingsUpdate(settings::CECEVENTS, getSetting(settings::CECEVENTS)); } HyperionDaemon::~HyperionDaemon() @@ -233,8 +234,6 @@ void HyperionDaemon::handleInstanceStateChange(InstanceState state, quint8 insta connect(_sslWebserver, &WebServer::publishService, _mDNSProvider, &MdnsProvider::publishService); #endif sslWsThread->start(); - - //startCecHandler(); } break; @@ -743,6 +742,7 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs } else if (settingsType == settings::CECEVENTS) { + Debug(_log, "startCecHandler"); startCecHandler(); } } @@ -923,7 +923,7 @@ void HyperionDaemon::startCecHandler() #if defined(ENABLE_CEC) if (_cecHandler == nullptr) { - _cecHandler = new CECHandler; + _cecHandler = new CECHandler(getSetting(settings::CECEVENTS)); QThread* cecHandlerThread = new QThread(this); cecHandlerThread->setObjectName("CECThread"); @@ -931,13 +931,10 @@ void HyperionDaemon::startCecHandler() connect(cecHandlerThread, &QThread::started, _cecHandler, &CECHandler::start); connect(cecHandlerThread, &QThread::finished, _cecHandler, &CECHandler::stop); + connect(this, &HyperionDaemon::settingsChanged, _cecHandler, &CECHandler::handleSettingsUpdate); + Info(_log, "CEC event 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"); @@ -949,13 +946,10 @@ 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"); diff --git a/src/hyperiond/systray.cpp b/src/hyperiond/systray.cpp index 0d715652..fa6ada36 100644 --- a/src/hyperiond/systray.cpp +++ b/src/hyperiond/systray.cpp @@ -44,6 +44,8 @@ SysTray::SysTray(HyperionDaemon *hyperiond) // instance changes connect(_instanceManager, &HyperionIManager::instanceStateChanged, this, &SysTray::handleInstanceStateChange); + + connect(this, &SysTray::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); } SysTray::~SysTray() @@ -88,12 +90,11 @@ void SysTray::createTrayIcon() // TODO: Check if can be done with SystemEvents suspendAction = new QAction(tr("&Suspend"), this); suspendAction->setIcon(QPixmap(":/suspend.svg")); - connect(suspendAction, &QAction::triggered, EventHandler::getInstance(), QOverload<>::of(&EventHandler::suspend)); + connect(suspendAction, &QAction::triggered, this, [this]() { emit signalEvent(Event::Suspend); }); resumeAction = new QAction(tr("&Resume"), this); resumeAction->setIcon(QPixmap(":/resume.svg")); - - connect(resumeAction, &QAction::triggered, EventHandler::getInstance(), &EventHandler::resume); + connect(resumeAction, &QAction::triggered, this, [this]() { emit signalEvent(Event::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 bab57bf0..137ee129 100644 --- a/src/hyperiond/systray.h +++ b/src/hyperiond/systray.h @@ -49,6 +49,9 @@ private slots: /// void handleInstanceStateChange(InstanceState state, quint8 instance, const QString& name); +signals: + void signalEvent(Event event); + private: void createTrayIcon();