mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Introduce Event Services (#1653)
* Allow to enable/disable suspend & lock event handling * Fix Windows * Refactor event handling incl.CEC * Revert "Auxiliary commit to revert individual files from 0d9a8b8a3a4a09609a339f54c7d8a9384c561282" This reverts commit 80737d926ad151a07b2493dd1685ed502975cb2e. * Support Events for Grabbers generically * Have CECEvent to actions configurable, further clean-ups * Remove handleEvent from V4L2grabber, as grabber will be stopped on suspend * Validate that one CEC Event can only trigger one action * MacOS lock/unlock added * fast windows fix * Corrections * Fix CodeQL findings * add macos lock/unlock handler * Migration of CEC-config and have default actions * Correct target_link_libraries * Include Foundation * macOS include AppKit * Support Scheduled Events, cleanups. * Fix destructing * Fix coredump during free * Consider additional error sceanrio * Fix missing code * install desktop icons * correct bash logic --------- Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com>
This commit is contained in:
@@ -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()
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include <grabber/qt/QtGrabber.h>
|
||||
|
||||
#include <utils/WeakConnect.h>
|
||||
#include <events/EventEnum.h>
|
||||
|
||||
#if defined(ENABLE_MF)
|
||||
#include <grabber/video/mediafoundation/MFGrabber.h>
|
||||
@@ -82,6 +83,7 @@
|
||||
|
||||
// api includes
|
||||
#include <api/JsonCB.h>
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
// auth manager
|
||||
#include <hyperion/AuthManager.h>
|
||||
@@ -110,6 +112,8 @@ JsonAPI::JsonAPI(QString peerAddress, Logger *log, bool localConnection, QObject
|
||||
_ledStreamTimer = new QTimer(this);
|
||||
|
||||
Q_INIT_RESOURCE(JSONRPC_schemas);
|
||||
|
||||
qRegisterMetaType<Event>("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
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include <libcec/cecloader.h>
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -12,13 +13,21 @@
|
||||
#include <QFile>
|
||||
|
||||
/* Enable to turn on detailed CEC logs */
|
||||
#define NO_VERBOSE_CEC
|
||||
#define NOVERBOSE_CEC
|
||||
|
||||
CECHandler::CECHandler()
|
||||
CECHandler::CECHandler(const QJsonDocument& config, QObject * parent)
|
||||
: QObject(parent)
|
||||
, _config(config)
|
||||
, _isInitialised(false)
|
||||
, _isEnabled(false)
|
||||
, _buttonReleaseDelayMs(CEC_BUTTON_TIMEOUT)
|
||||
, _buttonRepeatRateMs(0)
|
||||
, _doubleTapTimeoutMs(CEC_DOUBLE_TAP_TIMEOUT_MS)
|
||||
, _cecEventActionMap()
|
||||
{
|
||||
qRegisterMetaType<CECEvent>("CECEvent");
|
||||
qRegisterMetaType<Event>("Event");
|
||||
|
||||
_logger = Logger::getInstance("CEC");
|
||||
_logger = Logger::getInstance("EVENTS-CEC");
|
||||
|
||||
_cecCallbacks = getCallbacks();
|
||||
_cecConfig = getConfig();
|
||||
@@ -28,66 +37,151 @@ CECHandler::CECHandler()
|
||||
|
||||
CECHandler::~CECHandler()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::CECEVENTS)
|
||||
{
|
||||
if (_isInitialised)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
|
||||
_isEnabled = obj["enable"].toBool(false);
|
||||
Debug(_logger, "CEC Event handling is %s", _isEnabled? "enabled" : "disabled");
|
||||
|
||||
if (_isEnabled)
|
||||
{
|
||||
_buttonReleaseDelayMs = obj["buttonReleaseDelayMs"].toInt(CEC_BUTTON_TIMEOUT);
|
||||
_buttonRepeatRateMs = obj["buttonRepeatRateMs"].toInt(0);
|
||||
_doubleTapTimeoutMs = obj["doubleTapTimeoutMs"].toInt(CEC_DOUBLE_TAP_TIMEOUT_MS);
|
||||
|
||||
Debug(_logger, "Remote button press release time : %dms",_buttonReleaseDelayMs);
|
||||
Debug(_logger, "Remote button press repeat rate : %dms",_buttonRepeatRateMs);
|
||||
Debug(_logger, "Remote button press delay before repeating : %dms",_doubleTapTimeoutMs);
|
||||
|
||||
_cecConfig.iButtonReleaseDelayMs = static_cast<uint32_t>(_buttonReleaseDelayMs);
|
||||
_cecConfig.iButtonRepeatRateMs = static_cast<uint32_t>(_buttonRepeatRateMs);
|
||||
_cecConfig.iDoubleTapTimeoutMs = static_cast<uint32_t>(_doubleTapTimeoutMs);
|
||||
|
||||
_cecEventActionMap.clear();
|
||||
const QJsonArray actionItems = obj["actions"].toArray();
|
||||
if (!actionItems.isEmpty())
|
||||
{
|
||||
for (const QJsonValue &item : actionItems)
|
||||
{
|
||||
QString cecEvent = item.toObject().value("event").toString();
|
||||
QString action = item.toObject().value("action").toString();
|
||||
_cecEventActionMap.insert(cecEvent, stringToEvent(action));
|
||||
Debug(_logger, "CEC-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(cecEvent), QSTRING_CSTR(action));
|
||||
}
|
||||
}
|
||||
|
||||
if (!_cecEventActionMap.isEmpty())
|
||||
{
|
||||
enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_logger, "No CEC events to listen to are configured currently.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CECHandler::start()
|
||||
{
|
||||
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)
|
||||
_isInitialised = false;
|
||||
if (_cecAdapter == nullptr)
|
||||
{
|
||||
Error(_logger, "Failed to loading libcec.so");
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_logger, "CEC handler started");
|
||||
|
||||
auto adapters = getAdapters();
|
||||
if (adapters.isEmpty())
|
||||
{
|
||||
Error(_logger, "Failed to find CEC adapter");
|
||||
UnloadLibCec(_cecAdapter);
|
||||
_cecAdapter = nullptr;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_logger, "Auto detecting CEC adapter");
|
||||
bool opened = false;
|
||||
for (const auto & adapter : adapters)
|
||||
{
|
||||
printAdapter(adapter);
|
||||
|
||||
if (!opened && openAdapter(adapter))
|
||||
_cecAdapter = LibCecInitialise(&_cecConfig);
|
||||
if(_cecAdapter == nullptr)
|
||||
{
|
||||
Info(_logger, "CEC Handler initialized with adapter : %s", adapter.strComName);
|
||||
|
||||
opened = true;
|
||||
Error(_logger, "Failed loading libCEC library. CEC is not supported.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_isInitialised = true;
|
||||
}
|
||||
|
||||
handleSettingsUpdate(settings::CECEVENTS,_config);
|
||||
}
|
||||
return _isInitialised;
|
||||
}
|
||||
|
||||
void CECHandler::stop()
|
||||
{
|
||||
if (_cecAdapter != nullptr)
|
||||
{
|
||||
Info(_logger, "Stopping CEC handler");
|
||||
_cecAdapter->Close();
|
||||
UnloadLibCec(_cecAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
bool CECHandler::enable()
|
||||
{
|
||||
bool opened {false};
|
||||
if (_isInitialised)
|
||||
{
|
||||
const auto adapters = getAdapters();
|
||||
if (adapters.isEmpty())
|
||||
{
|
||||
Error(_logger, "Failed to find any CEC adapter. CEC event handling will be disabled.");
|
||||
_cecAdapter->Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
Info(_logger, "Auto detecting CEC adapter");
|
||||
for (const auto & adapter : adapters)
|
||||
{
|
||||
printAdapter(adapter);
|
||||
|
||||
if (!opened && openAdapter(adapter))
|
||||
{
|
||||
Info(_logger, "CEC adapter '%s', type: %s initialized." , adapter.strComName, _cecAdapter->ToString(adapter.adapterType));
|
||||
opened = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef VERBOSE_CEC
|
||||
std::cout << "Found Devices: " << scan().toStdString() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!opened)
|
||||
{
|
||||
UnloadLibCec(_cecAdapter);
|
||||
_cecAdapter = nullptr;
|
||||
Error(_logger, "Could not initialize any CEC adapter.");
|
||||
_cecAdapter->Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_cecAdapter->SetConfiguration(&_cecConfig))
|
||||
{
|
||||
Error(_logger, "Failed setting remote button press timing parameters");
|
||||
}
|
||||
QObject::connect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
Info(_logger, "CEC handler started");
|
||||
}
|
||||
|
||||
return opened;
|
||||
}
|
||||
|
||||
void CECHandler::stop()
|
||||
void CECHandler::disable()
|
||||
{
|
||||
if (_cecAdapter)
|
||||
if (_isInitialised)
|
||||
{
|
||||
Info(_logger, "Stopping CEC handler");
|
||||
|
||||
QObject::disconnect(this, &CECHandler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
|
||||
_cecAdapter->Close();
|
||||
UnloadLibCec(_cecAdapter);
|
||||
_cecAdapter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +191,6 @@ 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;
|
||||
|
||||
@@ -121,11 +214,11 @@ CECCallbacks CECHandler::getCallbacks() const
|
||||
|
||||
QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
|
||||
{
|
||||
if (!_cecAdapter)
|
||||
if (_cecAdapter == nullptr)
|
||||
return {};
|
||||
|
||||
QVector<CECAdapterDescriptor> descriptors(16);
|
||||
int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), descriptors.size(), nullptr, true /*quickscan*/);
|
||||
int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), static_cast<uint8_t>(descriptors.size()), nullptr, true /*quickscan*/);
|
||||
descriptors.resize(size);
|
||||
|
||||
return descriptors;
|
||||
@@ -133,15 +226,12 @@ QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
|
||||
|
||||
bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
||||
{
|
||||
if (!_cecAdapter)
|
||||
if (_cecAdapter == nullptr)
|
||||
return false;
|
||||
|
||||
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,28 +239,44 @@ 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)));
|
||||
Debug(_logger, "CEC Adapter:");
|
||||
Debug(_logger, "\tName : %s", descriptor.strComName);
|
||||
Debug(_logger, "\tPath : %s", descriptor.strComPath);
|
||||
if (descriptor.iVendorId != 0)
|
||||
{
|
||||
Debug(_logger, "\tVendor id: %04x", descriptor.iVendorId);
|
||||
}
|
||||
if (descriptor.iProductId != 0)
|
||||
{
|
||||
Debug(_logger, "\tProduct id: %04x", descriptor.iProductId);
|
||||
}
|
||||
if (descriptor.iFirmwareVersion != 0)
|
||||
{
|
||||
Debug(_logger, "\tFirmware id: %d", descriptor.iFirmwareVersion);
|
||||
}
|
||||
if (descriptor.adapterType != CEC::ADAPTERTYPE_UNKNOWN)
|
||||
{
|
||||
Debug(_logger, "\tType : %s", _cecAdapter->ToString(descriptor.adapterType));
|
||||
}
|
||||
}
|
||||
|
||||
QString CECHandler::scan() const
|
||||
{
|
||||
if (!_cecAdapter)
|
||||
if (_cecAdapter == nullptr)
|
||||
return {};
|
||||
|
||||
Info(_logger, "Starting CEC scan");
|
||||
|
||||
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<CECLogicalAddress>(address);
|
||||
|
||||
QJsonObject device;
|
||||
CECVendorId vendor = (CECVendorId)_cecAdapter->GetDeviceVendorId(logicalAddress);
|
||||
CECVendorId vendor = static_cast<CECVendorId>(_cecAdapter->GetDeviceVendorId(logicalAddress));
|
||||
CECPowerStatus power = _cecAdapter->GetDevicePowerStatus(logicalAddress);
|
||||
|
||||
device["name" ] = _cecAdapter->GetDeviceOSDName(logicalAddress).c_str();
|
||||
@@ -181,46 +287,48 @@ 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())
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
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 +338,36 @@ void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message)
|
||||
|
||||
void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key)
|
||||
{
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
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:
|
||||
case CEC::CEC_USER_CONTROL_CODE_F2_RED:
|
||||
case CEC::CEC_USER_CONTROL_CODE_F3_GREEN:
|
||||
case CEC::CEC_USER_CONTROL_CODE_F4_YELLOW:
|
||||
handler->triggerAction(adapter->ToString(key->keycode));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParameter data)
|
||||
void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParameter /* data */)
|
||||
{
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
Error(handler->_logger, QString("CECHandler::onCecAlert: %1")
|
||||
.arg(alert)
|
||||
.toLocal8Bit());
|
||||
Error(handler->_logger, QSTRING_CSTR(QString("CECHandler::onCecAlert: %1")
|
||||
.arg(alert)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -260,12 +375,10 @@ void CECHandler::onCecConfigurationChanged(void * context, const CECConfig * con
|
||||
{
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
Debug(handler->_logger, QString("CECHandler::onCecConfigurationChanged: %1")
|
||||
.arg(configuration->strDeviceName)
|
||||
.toLocal8Bit());
|
||||
Debug(handler->_logger, "CECHandler::onCecConfigurationChanged: %s", configuration->strDeviceName);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -278,9 +391,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 +405,25 @@ 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)
|
||||
switch (command->opcode) {
|
||||
case CEC::CEC_OPCODE_STANDBY:
|
||||
case CEC::CEC_OPCODE_SET_STREAM_PATH:
|
||||
{
|
||||
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source activated: %1")
|
||||
.arg(adapter->ToString(command->initiator)))
|
||||
);
|
||||
emit handler->cecEvent(CECEvent::On);
|
||||
handler->triggerAction(adapter->ToString(command->opcode));
|
||||
}
|
||||
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);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -327,15 +435,14 @@ void CECHandler::onCecSourceActivated(void * context, const CECLogicalAddress ad
|
||||
|
||||
#ifdef VERBOSE_CEC
|
||||
CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
|
||||
if (!handler)
|
||||
if (handler == nullptr)
|
||||
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
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ else()
|
||||
endif()
|
||||
|
||||
add_library(cechandler
|
||||
${CMAKE_SOURCE_DIR}/include/cec/CECEvent.h
|
||||
${CMAKE_SOURCE_DIR}/include/cec/CECHandler.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/cec/CECHandler.cpp
|
||||
)
|
||||
|
27
libsrc/events/CMakeLists.txt
Normal file
27
libsrc/events/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
add_library(events
|
||||
${CMAKE_SOURCE_DIR}/include/events/EventEnum.h
|
||||
${CMAKE_SOURCE_DIR}/include/events/EventHandler.h
|
||||
${CMAKE_SOURCE_DIR}/include/events/OsEventHandler.h
|
||||
${CMAKE_SOURCE_DIR}/include/events/EventScheduler.h
|
||||
${CMAKE_SOURCE_DIR}/libsrc/events/EventHandler.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/events/OsEventHandler.cpp
|
||||
${CMAKE_SOURCE_DIR}/libsrc/events/EventScheduler.cpp
|
||||
)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS DBus QUIET)
|
||||
if(Qt${QT_VERSION_MAJOR}DBus_FOUND)
|
||||
target_link_libraries(events Qt${QT_VERSION_MAJOR}::DBus)
|
||||
target_compile_definitions(events PRIVATE HYPERION_HAS_DBUS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(events
|
||||
hyperion-utils
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set_source_files_properties(OsEventHandler.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
|
||||
target_link_libraries(events "-framework AppKit")
|
||||
endif()
|
186
libsrc/events/EventHandler.cpp
Normal file
186
libsrc/events/EventHandler.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Process.h>
|
||||
#include <hyperion/HyperionIManager.h>
|
||||
|
||||
EventHandler::EventHandler()
|
||||
: _isSuspended(false)
|
||||
, _isIdle(false)
|
||||
{
|
||||
qRegisterMetaType<Event>("Event");
|
||||
_log = Logger::getInstance("EVENTS");
|
||||
|
||||
QObject::connect(this, &EventHandler::signalEvent, HyperionIManager::getInstance(), &HyperionIManager::handleEvent);
|
||||
}
|
||||
|
||||
EventHandler::~EventHandler()
|
||||
{
|
||||
QObject::disconnect(this, &EventHandler::signalEvent, HyperionIManager::getInstance(), &HyperionIManager::handleEvent);
|
||||
}
|
||||
|
||||
EventHandler* EventHandler::getInstance()
|
||||
{
|
||||
static EventHandler instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void EventHandler::suspend()
|
||||
{
|
||||
suspend(true);
|
||||
}
|
||||
|
||||
void EventHandler::suspend(bool sleep)
|
||||
{
|
||||
if (sleep)
|
||||
{
|
||||
if (!_isSuspended)
|
||||
{
|
||||
_isSuspended = true;
|
||||
Info(_log, "Suspend event received - Hyperion is going to sleep");
|
||||
emit signalEvent(Event::Suspend);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Suspend event ignored - already suspended");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isSuspended || _isIdle)
|
||||
{
|
||||
Info(_log, "Resume event received - Hyperion is going into working mode");
|
||||
emit signalEvent(Event::Resume);
|
||||
_isSuspended = false;
|
||||
_isIdle = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Resume event ignored - not in suspend nor idle mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::resume()
|
||||
{
|
||||
suspend(false);
|
||||
}
|
||||
|
||||
void EventHandler::toggleSuspend()
|
||||
{
|
||||
Debug(_log, "Toggle suspend event received");
|
||||
if (!_isSuspended)
|
||||
{
|
||||
suspend(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
suspend(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::idle()
|
||||
{
|
||||
idle(true);
|
||||
}
|
||||
|
||||
void EventHandler::idle(bool isIdle)
|
||||
{
|
||||
if (!_isSuspended)
|
||||
{
|
||||
if (isIdle)
|
||||
{
|
||||
if (!_isIdle)
|
||||
{
|
||||
_isIdle = true;
|
||||
Info(_log, "Idle event received");
|
||||
emit signalEvent(Event::Idle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isIdle)
|
||||
{
|
||||
Info(_log, "Resume from idle event recevied");
|
||||
emit signalEvent(Event::ResumeIdle);
|
||||
_isIdle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Idle event ignored - Hyperion is suspended");
|
||||
}
|
||||
}
|
||||
void EventHandler::resumeIdle()
|
||||
{
|
||||
idle(false);
|
||||
}
|
||||
|
||||
void EventHandler::toggleIdle()
|
||||
{
|
||||
Debug(_log, "Toggle idle event received");
|
||||
if (!_isIdle)
|
||||
{
|
||||
idle(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
idle(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EventHandler::handleEvent(Event event)
|
||||
{
|
||||
QObject *senderObj = QObject::sender();
|
||||
QString senderObjectClass;
|
||||
if (senderObj)
|
||||
{
|
||||
senderObjectClass = senderObj->metaObject()->className();
|
||||
} else
|
||||
{
|
||||
senderObjectClass = "unknown sender";
|
||||
}
|
||||
Debug(_log,"%s Event [%d] received from %s", eventToString(event), event, QSTRING_CSTR(senderObjectClass));
|
||||
|
||||
switch (event) {
|
||||
case Event::Suspend:
|
||||
suspend();
|
||||
break;
|
||||
|
||||
case Event::Resume:
|
||||
resume();
|
||||
break;
|
||||
|
||||
case Event::ToggleSuspend:
|
||||
toggleSuspend();
|
||||
break;
|
||||
|
||||
case Event::Idle:
|
||||
idle(true);
|
||||
break;
|
||||
|
||||
case Event::ResumeIdle:
|
||||
idle(false);
|
||||
break;
|
||||
|
||||
case Event::ToggleIdle:
|
||||
toggleIdle();
|
||||
break;
|
||||
|
||||
case Event::Reload:
|
||||
Process::restartHyperion(10);
|
||||
break;
|
||||
|
||||
case Event::Restart:
|
||||
Process::restartHyperion(11);
|
||||
break;
|
||||
|
||||
default:
|
||||
Error(_log,"Unkonwn Event '%d' received", event);
|
||||
break;
|
||||
}
|
||||
}
|
153
libsrc/events/EventScheduler.cpp
Normal file
153
libsrc/events/EventScheduler.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "events/EventScheduler.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <events/EventHandler.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
EventScheduler::EventScheduler()
|
||||
: _isEnabled(false)
|
||||
{
|
||||
qRegisterMetaType<Event>("Event");
|
||||
_log = Logger::getInstance("EVENTS-SCHED");
|
||||
|
||||
QObject::connect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
}
|
||||
|
||||
EventScheduler::~EventScheduler()
|
||||
{
|
||||
QObject::disconnect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
clearTimers();
|
||||
Info(_log, "Event scheduler stopped");
|
||||
}
|
||||
|
||||
void EventScheduler::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::SCHEDEVENTS)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
|
||||
_isEnabled = obj["enable"].toBool(false);
|
||||
Debug(_log, "Event scheduler is %s", _isEnabled? "enabled" : "disabled");
|
||||
|
||||
if (_isEnabled)
|
||||
{
|
||||
_scheduledEvents.clear();
|
||||
|
||||
const QJsonArray actionItems = obj["actions"].toArray();
|
||||
if (!actionItems.isEmpty())
|
||||
{
|
||||
timeEvent timeEvent;
|
||||
for (const QJsonValue &item : actionItems)
|
||||
{
|
||||
QString action = item.toObject().value("action").toString();
|
||||
timeEvent.action = stringToEvent(action);
|
||||
|
||||
QString event = item.toObject().value("event").toString();
|
||||
timeEvent.time = QTime::fromString(event,"hh:mm");
|
||||
if (timeEvent.time.isValid())
|
||||
{
|
||||
_scheduledEvents.append(timeEvent);
|
||||
Debug(_log, "Time-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action));
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Error in configured time : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_scheduledEvents.isEmpty())
|
||||
{
|
||||
enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
Warning(_log, "No scheduled events to listen to are configured currently.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
disable();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool EventScheduler::enable()
|
||||
{
|
||||
bool enabled {false};
|
||||
|
||||
clearTimers();
|
||||
for (int i = 0; i < _scheduledEvents.size(); ++i)
|
||||
{
|
||||
QTimer* timer = new QTimer(this);
|
||||
timer->setTimerType(Qt::PreciseTimer);
|
||||
_timers.append(timer);
|
||||
|
||||
// Calculate the milliseconds until the next occurrence of the scheduled time
|
||||
int milliseconds = getMillisecondsToNextScheduledTime(_scheduledEvents.at(i).time);
|
||||
timer->start(milliseconds);
|
||||
|
||||
QObject::connect(timer, &QTimer::timeout, this, [this, i]() { handleEvent(i); });
|
||||
}
|
||||
|
||||
enabled = true;
|
||||
Info(_log, "Event scheduler started");
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void EventScheduler::disable()
|
||||
{
|
||||
Info(_log, "Disabling event scheduler");
|
||||
clearTimers();
|
||||
}
|
||||
|
||||
void EventScheduler::clearTimers()
|
||||
{
|
||||
for (QTimer *timer : std::as_const(_timers)) {
|
||||
timer->disconnect();
|
||||
timer->stop();
|
||||
delete timer;
|
||||
}
|
||||
_timers.clear();
|
||||
}
|
||||
|
||||
void EventScheduler::handleEvent(int timerIndex)
|
||||
{
|
||||
QTime time = _scheduledEvents.at(timerIndex).time;
|
||||
Event action = _scheduledEvents.at(timerIndex).action;
|
||||
Debug(_log, "Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(time.toString()), eventToString(action) );
|
||||
if ( action != Event::Unknown )
|
||||
{
|
||||
emit signalEvent(action);
|
||||
}
|
||||
|
||||
// Retrigger the timer for the next occurrence
|
||||
QTimer* timer = _timers.at(timerIndex);
|
||||
int milliseconds = getMillisecondsToNextScheduledTime(time);
|
||||
timer->start(milliseconds);
|
||||
}
|
||||
|
||||
int EventScheduler::getMillisecondsToNextScheduledTime(const QTime& scheduledTime)
|
||||
{
|
||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||
QTime currentTime = currentDateTime.time();
|
||||
|
||||
QDateTime nextOccurrence = currentDateTime;
|
||||
nextOccurrence.setTime(scheduledTime);
|
||||
|
||||
// If the scheduled time has already passed for today, schedule it for tomorrow
|
||||
if (currentTime > scheduledTime)
|
||||
{
|
||||
nextOccurrence = nextOccurrence.addDays(1);
|
||||
}
|
||||
|
||||
int milliseconds = currentDateTime.msecsTo(nextOccurrence);
|
||||
return (milliseconds > 0) ? milliseconds : 0;
|
||||
}
|
626
libsrc/events/OsEventHandler.cpp
Normal file
626
libsrc/events/OsEventHandler.cpp
Normal file
@@ -0,0 +1,626 @@
|
||||
#include "events/OsEventHandler.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <events/EventHandler.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <QCoreApplication>
|
||||
#include <QWidget>
|
||||
#include <windows.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#pragma comment( lib, "wtsapi32.lib" )
|
||||
#elif defined(__APPLE__)
|
||||
#include <AppKit/AppKit.h>
|
||||
#endif
|
||||
|
||||
OsEventHandlerBase::OsEventHandlerBase()
|
||||
: _isSuspendEnabled(false)
|
||||
, _isLockEnabled(false)
|
||||
, _isSuspendOnLock(false)
|
||||
, _isSuspendRegistered(false)
|
||||
, _isLockRegistered(false)
|
||||
{
|
||||
qRegisterMetaType<Event>("Event");
|
||||
_log = Logger::getInstance("EVENTS-OS");
|
||||
|
||||
QObject::connect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
}
|
||||
|
||||
OsEventHandlerBase::~OsEventHandlerBase()
|
||||
{
|
||||
QObject::disconnect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent);
|
||||
|
||||
OsEventHandlerBase::unregisterLockHandler();
|
||||
OsEventHandlerBase::unregisterOsEventHandler();
|
||||
|
||||
Info(_log, "Operating System event handler stopped");
|
||||
}
|
||||
|
||||
void OsEventHandlerBase::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::OSEVENTS)
|
||||
{
|
||||
const QJsonObject& obj = config.object();
|
||||
|
||||
//Suspend on lock or go into idle mode
|
||||
bool prevIsSuspendOnLock = _isSuspendOnLock;
|
||||
_isSuspendOnLock = obj["suspendOnLockEnable"].toBool(false);
|
||||
|
||||
//Handle OS event related configurations
|
||||
_isSuspendEnabled = obj["suspendEnable"].toBool(true);
|
||||
if (_isSuspendEnabled)
|
||||
{
|
||||
// Listen to suspend/resume/idle events received by the OS
|
||||
registerOsEventHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
unregisterOsEventHandler();
|
||||
}
|
||||
|
||||
_isLockEnabled = obj["lockEnable"].toBool(true);
|
||||
if (_isLockEnabled || _isSuspendOnLock != prevIsSuspendOnLock)
|
||||
{
|
||||
// Listen to lock/screensaver events received by the OS
|
||||
registerLockHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
unregisterLockHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OsEventHandlerBase::suspend(bool sleep)
|
||||
{
|
||||
if (sleep)
|
||||
{
|
||||
emit signalEvent(Event::Suspend);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit signalEvent(Event::Resume);
|
||||
}
|
||||
}
|
||||
|
||||
void OsEventHandlerBase::lock(bool isLocked)
|
||||
{
|
||||
if (isLocked)
|
||||
{
|
||||
if (_isSuspendOnLock)
|
||||
{
|
||||
emit signalEvent(Event::Suspend);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit signalEvent(Event::Idle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isSuspendOnLock)
|
||||
{
|
||||
emit signalEvent(Event::Resume);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit signalEvent(Event::ResumeIdle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
OsEventHandlerWindows::OsEventHandlerWindows()
|
||||
: _notifyHandle(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
OsEventHandlerWindows::~OsEventHandlerWindows()
|
||||
{
|
||||
unregisterLockHandler();
|
||||
unregisterOsEventHandler();
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, qintptr* /*result*/)
|
||||
#else
|
||||
bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, long int* /*result*/)
|
||||
#endif
|
||||
{
|
||||
MSG* msg = static_cast<MSG*>(message);
|
||||
|
||||
switch (msg->message)
|
||||
{
|
||||
case WM_WTSSESSION_CHANGE:
|
||||
switch (msg->wParam)
|
||||
{
|
||||
case WTS_SESSION_LOCK:
|
||||
emit lock(true);
|
||||
return true;
|
||||
break;
|
||||
case WTS_SESSION_UNLOCK:
|
||||
emit lock(false);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_POWERBROADCAST:
|
||||
switch (msg->wParam)
|
||||
{
|
||||
case PBT_APMRESUMESUSPEND:
|
||||
emit suspend(false);
|
||||
return true;
|
||||
break;
|
||||
case PBT_APMSUSPEND:
|
||||
emit suspend(true);
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OsEventHandlerWindows::registerOsEventHandler()
|
||||
{
|
||||
bool isRegistered{ _isSuspendRegistered };
|
||||
if (!_isSuspendRegistered)
|
||||
{
|
||||
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
||||
_notifyHandle = RegisterSuspendResumeNotification(handle, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
if (_notifyHandle != NULL)
|
||||
{
|
||||
QCoreApplication::instance()->installNativeEventFilter(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for suspend/resume events!");
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isSuspendRegistered = true;
|
||||
}
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerWindows::unregisterOsEventHandler()
|
||||
{
|
||||
if (_isSuspendRegistered)
|
||||
{
|
||||
if (_notifyHandle != NULL)
|
||||
{
|
||||
QCoreApplication::instance()->removeNativeEventFilter(this);
|
||||
UnregisterSuspendResumeNotification(_notifyHandle);
|
||||
}
|
||||
_notifyHandle = NULL;
|
||||
_isSuspendRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool OsEventHandlerWindows::registerLockHandler()
|
||||
{
|
||||
bool isRegistered{ _isLockRegistered };
|
||||
if (!_isLockRegistered)
|
||||
{
|
||||
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
||||
if (WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION))
|
||||
{
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for lock/unlock events!");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isLockRegistered = true;
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerWindows::unregisterLockHandler()
|
||||
{
|
||||
if (_isLockRegistered)
|
||||
{
|
||||
auto handle = reinterpret_cast<HWND> (_widget.winId());
|
||||
WTSUnRegisterSessionNotification(handle);
|
||||
_isLockRegistered = false;
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
#include <csignal>
|
||||
|
||||
OsEventHandlerLinux* OsEventHandlerLinux::getInstance()
|
||||
{
|
||||
static OsEventHandlerLinux instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
OsEventHandlerLinux::OsEventHandlerLinux()
|
||||
{
|
||||
signal(SIGUSR1, static_signaleHandler);
|
||||
signal(SIGUSR2, static_signaleHandler);
|
||||
}
|
||||
|
||||
void OsEventHandlerLinux::handleSignal (int signum)
|
||||
{
|
||||
if (signum == SIGUSR1)
|
||||
{
|
||||
suspend(true);
|
||||
}
|
||||
else if (signum == SIGUSR2)
|
||||
{
|
||||
suspend(false);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HYPERION_HAS_DBUS)
|
||||
#include <QDBusConnection>
|
||||
|
||||
struct dBusSignals
|
||||
{
|
||||
QString service;
|
||||
QString path;
|
||||
QString interface;
|
||||
QString name;
|
||||
};
|
||||
|
||||
typedef QMultiMap<QString, dBusSignals> DbusSignalsMap;
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const DbusSignalsMap dbusSignals = {
|
||||
//system signals
|
||||
{"Suspend", {"org.freedesktop.login1","/org/freedesktop/login1","org.freedesktop.login1.Manager","PrepareForSleep"}},
|
||||
|
||||
//Session signals
|
||||
{"ScreenSaver", {"org.freedesktop.ScreenSaver","/org/freedesktop/ScreenSaver","org.freedesktop.ScreenSaver","ActiveChanged"}},
|
||||
{"ScreenSaver", {"org.gnome.ScreenSaver","/org/gnome/ScreenSaver","org.gnome.ScreenSaver","ActiveChanged"}},
|
||||
};
|
||||
} //End of constants
|
||||
|
||||
bool OsEventHandlerLinux::registerOsEventHandler()
|
||||
{
|
||||
|
||||
bool isRegistered {_isSuspendRegistered};
|
||||
if (!_isSuspendRegistered)
|
||||
{
|
||||
QDBusConnection systemBus = QDBusConnection::systemBus();
|
||||
if (!systemBus.isConnected())
|
||||
{
|
||||
Info(_log, "The suspend/resume feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
QString service = dbusSignals.find("Suspend").value().service;
|
||||
if (systemBus.connect(service,
|
||||
dbusSignals.find("Suspend").value().path,
|
||||
dbusSignals.find("Suspend").value().interface,
|
||||
dbusSignals.find("Suspend").value().name,
|
||||
this, SLOT(suspend(bool))))
|
||||
{
|
||||
Debug(_log, "Registered for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
}
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isSuspendRegistered = true;
|
||||
}
|
||||
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerLinux::unregisterOsEventHandler()
|
||||
{
|
||||
if (_isSuspendRegistered)
|
||||
{
|
||||
QDBusConnection systemBus = QDBusConnection::systemBus();
|
||||
if (!systemBus.isConnected())
|
||||
{
|
||||
Info(_log, "The suspend/resume feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
QString service = dbusSignals.find("Suspend").value().service;
|
||||
if (systemBus.disconnect(service,
|
||||
dbusSignals.find("Suspend").value().path,
|
||||
dbusSignals.find("Suspend").value().interface,
|
||||
dbusSignals.find("Suspend").value().name,
|
||||
this, SLOT(suspend(bool))))
|
||||
{
|
||||
Debug(_log, "Unregistered for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
_isSuspendRegistered = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for suspend/resume events via service: %s", QSTRING_CSTR(service));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OsEventHandlerLinux::registerLockHandler()
|
||||
{
|
||||
bool isRegistered {_isLockRegistered};
|
||||
|
||||
if (!_isLockRegistered)
|
||||
{
|
||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||
if (!sessionBus.isConnected())
|
||||
{
|
||||
Info(_log, "The lock/unlock feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
DbusSignalsMap::const_iterator iter = dbusSignals.find("ScreenSaver");
|
||||
while (iter != dbusSignals.end() && iter.key() == "ScreenSaver") {
|
||||
QString service = iter.value().service;
|
||||
if (sessionBus.connect(service,
|
||||
iter.value().path,
|
||||
iter.value().interface,
|
||||
iter.value().name,
|
||||
this, SLOT(lock(bool))))
|
||||
{
|
||||
Debug(_log, "Registered for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isLockRegistered = true;
|
||||
}
|
||||
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerLinux::unregisterLockHandler()
|
||||
{
|
||||
bool isUnregistered {false};
|
||||
|
||||
if (_isLockRegistered)
|
||||
{
|
||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||
if (!sessionBus.isConnected())
|
||||
{
|
||||
Info(_log, "The lock/unlock feature is not supported by your system configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
DbusSignalsMap::const_iterator iter = dbusSignals.find("ScreenSaver");
|
||||
while (iter != dbusSignals.end() && iter.key() == "ScreenSaver") {
|
||||
QString service = iter.value().service;
|
||||
if (sessionBus.disconnect(service,
|
||||
iter.value().path,
|
||||
iter.value().interface,
|
||||
iter.value().name,
|
||||
this, SLOT(lock(bool))))
|
||||
{
|
||||
Debug(_log, "Unregistered for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
isUnregistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for lock/unlock events via service: %s", QSTRING_CSTR(service));
|
||||
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (isUnregistered)
|
||||
{
|
||||
_isLockRegistered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HYPERION_HAS_DBUS
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
OsEventHandlerMacOS::OsEventHandlerMacOS()
|
||||
: _sleepEventHandler(nullptr)
|
||||
, _lockEventHandler(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@interface SleepEvents : NSObject
|
||||
{
|
||||
OsEventHandlerMacOS *_eventHandler;
|
||||
}
|
||||
- (id)initSleepEvents:(OsEventHandlerMacOS *)osEventHandler;
|
||||
@end
|
||||
|
||||
@implementation SleepEvents
|
||||
- (id)initSleepEvents:(OsEventHandlerMacOS *)osEventHandler
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_eventHandler = osEventHandler;
|
||||
id notifCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
[notifCenter addObserver:self selector:@selector(receiveSleepWake:) name:NSWorkspaceWillSleepNotification object:nil];
|
||||
[notifCenter addObserver:self selector:@selector(receiveSleepWake:) name:NSWorkspaceDidWakeNotification object:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
|
||||
_eventHandler = nullptr;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) receiveSleepWake:(NSNotification*)notification
|
||||
{
|
||||
if (!_eventHandler) return;
|
||||
if (notification.name == NSWorkspaceWillSleepNotification)
|
||||
{
|
||||
_eventHandler->suspend(true);
|
||||
}
|
||||
else if (notification.name == NSWorkspaceDidWakeNotification)
|
||||
{
|
||||
_eventHandler->suspend(false);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
bool OsEventHandlerMacOS::registerOsEventHandler()
|
||||
{
|
||||
bool isRegistered {_isSuspendRegistered};
|
||||
if (!_isSuspendRegistered)
|
||||
{
|
||||
_sleepEventHandler = [[SleepEvents alloc] initSleepEvents:this];
|
||||
if (_sleepEventHandler)
|
||||
{
|
||||
Debug(_log, "Registered for suspend/resume events");
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for suspend/resume events");
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isSuspendRegistered = true;
|
||||
}
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerMacOS::unregisterOsEventHandler()
|
||||
{
|
||||
if (_isSuspendRegistered && _sleepEventHandler)
|
||||
{
|
||||
[(SleepEvents *)_sleepEventHandler release], _sleepEventHandler = nil;
|
||||
if (!_sleepEventHandler)
|
||||
{
|
||||
Debug(_log, "Unregistered for suspend/resume events");
|
||||
_isSuspendRegistered = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for suspend/resume events");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@interface LockEvents : NSObject
|
||||
{
|
||||
OsEventHandlerMacOS *_eventHandler;
|
||||
}
|
||||
- (id)initLockEvents:(OsEventHandlerMacOS *)osEventHandler;
|
||||
@end
|
||||
|
||||
@implementation LockEvents
|
||||
- (id)initLockEvents:(OsEventHandlerMacOS *)osEventHandler
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_eventHandler = osEventHandler;
|
||||
id defCenter = [NSDistributedNotificationCenter defaultCenter];
|
||||
[defCenter addObserver:self selector:@selector(receiveLockUnlock:) name:@"com.apple.screenIsLocked" object:nil];
|
||||
[defCenter addObserver:self selector:@selector(receiveLockUnlock:) name:@"com.apple.screenIsUnlocked" object:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
|
||||
_eventHandler = nullptr;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void) receiveLockUnlock:(NSNotification*)notification
|
||||
{
|
||||
if (!_eventHandler) return;
|
||||
if (CFEqual(notification.name, CFSTR("com.apple.screenIsLocked")))
|
||||
{
|
||||
_eventHandler->lock(true);
|
||||
}
|
||||
else if (CFEqual(notification.name, CFSTR("com.apple.screenIsUnlocked")))
|
||||
{
|
||||
_eventHandler->lock(false);
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
bool OsEventHandlerMacOS::registerLockHandler()
|
||||
{
|
||||
bool isRegistered{ _isLockRegistered };
|
||||
if (!_isLockRegistered)
|
||||
{
|
||||
_lockEventHandler = [[LockEvents alloc] initLockEvents:this];
|
||||
if (_lockEventHandler)
|
||||
{
|
||||
Debug(_log, "Registered for lock/unlock events");
|
||||
isRegistered = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not register for lock/unlock events!");
|
||||
}
|
||||
}
|
||||
|
||||
if (isRegistered)
|
||||
{
|
||||
_isLockRegistered = true;
|
||||
}
|
||||
return isRegistered;
|
||||
}
|
||||
|
||||
void OsEventHandlerMacOS::unregisterLockHandler()
|
||||
{
|
||||
if (_isLockRegistered && _lockEventHandler)
|
||||
{
|
||||
[(LockEvents *)_lockEventHandler release], _lockEventHandler = nil;
|
||||
if (!_lockEventHandler)
|
||||
{
|
||||
Debug(_log, "Unregistered for lock/unlock events");
|
||||
_isLockRegistered = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Could not unregister for lock/unlock events");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -15,10 +15,13 @@ VideoWrapper::VideoWrapper()
|
||||
{
|
||||
// register the image type
|
||||
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
|
||||
qRegisterMetaType<Event>("Event");
|
||||
|
||||
// Handle the image in the captured thread (Media Foundation/V4L2) using a direct connection
|
||||
connect(&_grabber, SIGNAL(newFrame(const Image<ColorRgb>&)), this, SLOT(newFrame(const Image<ColorRgb>&)), Qt::DirectConnection);
|
||||
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||
|
||||
connect(&_grabber, SIGNAL(readError(const char*)), this, SLOT(readError(const char*)), Qt::DirectConnection);
|
||||
}
|
||||
|
||||
VideoWrapper::~VideoWrapper()
|
||||
@@ -37,15 +40,6 @@ void VideoWrapper::stop()
|
||||
GrabberWrapper::stop();
|
||||
}
|
||||
|
||||
#if defined(ENABLE_CEC) && !defined(ENABLE_MF)
|
||||
|
||||
void VideoWrapper::handleCecEvent(CECEvent event)
|
||||
{
|
||||
_grabber.handleCecEvent(event);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
|
||||
{
|
||||
if(type == settings::V4L2 && _grabberName.startsWith("V4L2"))
|
||||
@@ -100,11 +94,6 @@ void VideoWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument
|
||||
obj["hardware_saturation"].toInt(0),
|
||||
obj["hardware_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));
|
||||
|
||||
|
@@ -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<ColorRgb> 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,6 @@ bool V4L2Grabber::reload(bool force)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_CEC)
|
||||
|
||||
void V4L2Grabber::handleCecEvent(CECEvent event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case CECEvent::On :
|
||||
Debug(_log,"CEC on event received");
|
||||
_cecStandbyActivated = false;
|
||||
return;
|
||||
case CECEvent::Off :
|
||||
Debug(_log,"CEC off event received");
|
||||
_cecStandbyActivated = true;
|
||||
return;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QJsonArray V4L2Grabber::discover(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
@@ -49,6 +49,7 @@ add_library(hyperion
|
||||
|
||||
target_link_libraries(hyperion
|
||||
blackborder
|
||||
events
|
||||
hyperion-utils
|
||||
leddevice
|
||||
database
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
// utils includes
|
||||
#include <utils/GlobalSignals.h>
|
||||
#include <events/EventHandler.h>
|
||||
|
||||
// qt
|
||||
#include <QTimer>
|
||||
@@ -49,6 +50,8 @@ GrabberWrapper::GrabberWrapper(const QString& grabberName, Grabber * ggrabber, i
|
||||
|
||||
// listen for source requests
|
||||
connect(GlobalSignals::getInstance(), &GlobalSignals::requestSource, this, &GrabberWrapper::handleSourceRequest);
|
||||
|
||||
QObject::connect(EventHandler::getInstance(), &EventHandler::signalEvent, this, &GrabberWrapper::handleEvent);
|
||||
}
|
||||
|
||||
GrabberWrapper::~GrabberWrapper()
|
||||
@@ -83,6 +86,11 @@ void GrabberWrapper::stop()
|
||||
}
|
||||
}
|
||||
|
||||
void GrabberWrapper::handleEvent(Event event)
|
||||
{
|
||||
_ggrabber->handleEvent(event);
|
||||
}
|
||||
|
||||
bool GrabberWrapper::isActive() const
|
||||
{
|
||||
return _timer->isActive();
|
||||
|
@@ -63,23 +63,38 @@ void HyperionIManager::stopAll()
|
||||
}
|
||||
}
|
||||
|
||||
void HyperionIManager::suspend()
|
||||
void HyperionIManager::handleEvent(Event event)
|
||||
{
|
||||
Info(_log,"Suspend all instances and enabled components");
|
||||
QMap<quint8, Hyperion*> 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;
|
||||
|
||||
case Event::Resume:
|
||||
toggleSuspend(false);
|
||||
break;
|
||||
|
||||
case Event::Idle:
|
||||
toggleIdle(true);
|
||||
break;
|
||||
|
||||
case Event::ResumeIdle:
|
||||
toggleIdle(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HyperionIManager::resume()
|
||||
void HyperionIManager::toggleSuspend(bool isSuspend)
|
||||
{
|
||||
Info(_log,"Resume all instances and enabled components");
|
||||
Info(_log,"Put all instances in %s state", isSuspend ? "suspend" : "working");
|
||||
QMap<quint8, Hyperion*> instCopy = _runningInstances;
|
||||
for(const auto instance : instCopy)
|
||||
{
|
||||
emit instance->suspendRequest(false);
|
||||
emit instance->suspendRequest(isSuspend);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -894,6 +894,44 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config)
|
||||
Debug(_log, "LED-Device records migrated");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.contains("cecEvents"))
|
||||
{
|
||||
bool isCECEnabled {false};
|
||||
if (config.contains("grabberV4L2"))
|
||||
{
|
||||
QJsonObject newGrabberV4L2Config = config["grabberV4L2"].toObject();
|
||||
if (newGrabberV4L2Config.contains("cecDetection"))
|
||||
{
|
||||
isCECEnabled = newGrabberV4L2Config.value("cecDetection").toBool(false);
|
||||
newGrabberV4L2Config.remove("cecDetection");
|
||||
config["grabberV4L2"] = newGrabberV4L2Config;
|
||||
|
||||
QJsonObject newGCecEventsConfig = config["cecEvents"].toObject();
|
||||
newGCecEventsConfig["enable"] = isCECEnabled;
|
||||
if (!newGCecEventsConfig.contains("actions"))
|
||||
{
|
||||
QJsonObject action1
|
||||
{
|
||||
{"action", "Suspend"},
|
||||
{"event", "standby"}
|
||||
};
|
||||
QJsonObject action2
|
||||
{
|
||||
{"action", "Resume"},
|
||||
{"event", "set stream path"}
|
||||
};
|
||||
|
||||
QJsonArray actions { action1, action2 };
|
||||
newGCecEventsConfig.insert("actions",actions);
|
||||
}
|
||||
config["cecEvents"] = newGCecEventsConfig;
|
||||
|
||||
migrated = true;
|
||||
Debug(_log, "CEC configuration records migrated");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -90,6 +90,18 @@
|
||||
"leds":
|
||||
{
|
||||
"$ref": "schema-leds.json"
|
||||
},
|
||||
"osEvents":
|
||||
{
|
||||
"$ref": "schema-osEvents.json"
|
||||
},
|
||||
"cecEvents":
|
||||
{
|
||||
"$ref": "schema-cecEvents.json"
|
||||
},
|
||||
"schedEvents":
|
||||
{
|
||||
"$ref": "schema-schedEvents.json"
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
|
@@ -23,5 +23,9 @@
|
||||
<file alias="schema-leds.json">schema/schema-leds.json</file>
|
||||
<file alias="schema-instCapture.json">schema/schema-instCapture.json</file>
|
||||
<file alias="schema-network.json">schema/schema-network.json</file>
|
||||
<file alias="schema-eventActions.json">schema/schema-eventActions.json</file>
|
||||
<file alias="schema-schedEvents.json">schema/schema-schedEvents.json</file>
|
||||
<file alias="schema-osEvents.json">schema/schema-osEvents.json</file>
|
||||
<file alias="schema-cecEvents.json">schema/schema-cecEvents.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
95
libsrc/hyperion/schema/schema-cecEvents.json
Normal file
95
libsrc/hyperion/schema/schema-cecEvents.json
Normal file
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_general_enable_title",
|
||||
"default": false,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"buttonReleaseDelayMs": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"title": "edt_conf_cec_button_release_delay_ms_title",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 500,
|
||||
"step": 50,
|
||||
"default": 0,
|
||||
"required": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"buttonRepeatRateMs": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"title": "edt_conf_cec_button_repeat_rate_ms_title",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 250,
|
||||
"step": 10,
|
||||
"default": 0,
|
||||
"required": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"doubleTapTimeoutMs": {
|
||||
"type": "integer",
|
||||
"format": "stepper",
|
||||
"title": "edt_conf_cec_double_tap_timeout_ms_title",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 50,
|
||||
"maximum": 1000,
|
||||
"step": 50,
|
||||
"default": 200,
|
||||
"required": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_cec_actions_header_title",
|
||||
"minItems": 0,
|
||||
"required": false,
|
||||
"propertyOrder": 5,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"title": "edt_conf_cec_actions_header_item_title",
|
||||
"properties": {
|
||||
"event": {
|
||||
"type": "string",
|
||||
"title": "edt_conf_cec_event_title",
|
||||
"enum": [
|
||||
"standby",
|
||||
"set stream path",
|
||||
"F1(blue)",
|
||||
"F2 (red)",
|
||||
"F3 (green)",
|
||||
"F4 (yellow)"
|
||||
],
|
||||
"options": {
|
||||
"enum_titles": [
|
||||
"edt_conf_enum_cec_opcode_standby",
|
||||
"edt_conf_enum_cec_opcode_set stream path",
|
||||
"edt_conf_enum_cec_key_f1_blue",
|
||||
"edt_conf_enum_cec_key_f2_red",
|
||||
"edt_conf_enum_cec_key_f3_green",
|
||||
"edt_conf_enum_cec_key_f4_yellow"
|
||||
]
|
||||
},
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"action": {
|
||||
"$ref": "schema-eventActions.json"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
25
libsrc/hyperion/schema/schema-eventActions.json
Normal file
25
libsrc/hyperion/schema/schema-eventActions.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "string",
|
||||
"title": "edt_conf_action_title",
|
||||
"enum": [
|
||||
"Suspend",
|
||||
"Resume",
|
||||
"ToggleSuspend",
|
||||
"Idle",
|
||||
"ResumeIdle",
|
||||
"ToggleIdle",
|
||||
"Restart"
|
||||
],
|
||||
"options": {
|
||||
"enum_titles": [
|
||||
"edt_conf_enum_action_suspend",
|
||||
"edt_conf_enum_action_resume",
|
||||
"edt_conf_enum_action_toggleSuspend",
|
||||
"edt_conf_enum_action_idle",
|
||||
"edt_conf_enum_action_resumeIdle",
|
||||
"edt_conf_enum_action_toggleIdle",
|
||||
"edt_conf_enum_action_restart"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -212,21 +212,13 @@
|
||||
"required": true,
|
||||
"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
|
||||
|
34
libsrc/hyperion/schema/schema-osEvents.json
Normal file
34
libsrc/hyperion/schema/schema-osEvents.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"type" : "object",
|
||||
"required" : true,
|
||||
"title" : "edt_conf_os_events_heading_title",
|
||||
"properties": {
|
||||
"suspendEnable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_os_events_suspendEnable_title",
|
||||
"default": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"lockEnable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_os_events_lockEnable_title",
|
||||
"default": true,
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"suspendOnLockEnable": {
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"title": "edt_conf_os_events_suspendOnLockEnable_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"lockEnable": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 3
|
||||
}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
40
libsrc/hyperion/schema/schema-schedEvents.json
Normal file
40
libsrc/hyperion/schema/schema-schedEvents.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"enable": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"title": "edt_conf_general_enable_title",
|
||||
"default": false,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"title": "edt_conf_sched_actions_header_title",
|
||||
"minItems": 0,
|
||||
"required": false,
|
||||
"propertyOrder": 2,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"title": "edt_conf_sched_actions_header_item_title",
|
||||
"properties": {
|
||||
"event": {
|
||||
"type": "string",
|
||||
"format": "time",
|
||||
"default": "23:00",
|
||||
"title": "edt_conf_time_event_title",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"action": {
|
||||
"$ref": "schema-eventActions.json"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
Reference in New Issue
Block a user