diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a1da6acd..1a2f1d8e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ Note: The wizard will configure an APIv2 capable bridge always with Entertainmen - Fixed that the Matrix effect finds its image - Thanks @lsellens - MDNSBrower - Fixed, if timeout while resolving host occurs - Non image updates ignored blacklisted LEDs (#1634) +- Fixed that Windows OsEvents failed in non-GUI mode (#1671) ##### LED-Devices diff --git a/assets/webconfig/js/content_events.js b/assets/webconfig/js/content_events.js index 7f7201e27..787b9289d 100644 --- a/assets/webconfig/js/content_events.js +++ b/assets/webconfig/js/content_events.js @@ -1,6 +1,7 @@ $(document).ready(function () { performTranslation(); + let isGuiMode = window.sysInfo.hyperion.isGuiMode; const CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1); let conf_editor_osEvents = null; @@ -76,6 +77,12 @@ $(document).ready(function () { osEvents: window.schema.osEvents }, true, true); + conf_editor_osEvents.on('ready', function () { + if (!isGuiMode) { + showInputOptionsForKey(conf_editor_osEvents, "osEvents", "suspendEnable", false); + } + }); + conf_editor_osEvents.on('change', function () { conf_editor_osEvents.validate().length || window.readOnlyMode ? $('#btn_submit_os_events').prop('disabled', true) : $('#btn_submit_os_events').prop('disabled', false); }); diff --git a/assets/webconfig/js/ui_utils.js b/assets/webconfig/js/ui_utils.js index d2a998dc9..0061dde96 100644 --- a/assets/webconfig/js/ui_utils.js +++ b/assets/webconfig/js/ui_utils.js @@ -1224,6 +1224,7 @@ function getSystemInfo() { info += '- Avail Services: ' + window.serverInfo.services + '\n'; info += '- Config path: ' + shy.rootPath + '\n'; info += '- Database: ' + (shy.readOnlyMode ? "ready-only" : "read/write") + '\n'; + info += '- Mode: ' + (shy.isGuiMode ? "GUI" : "Non-GUI") + '\n'; info += '\n'; diff --git a/include/events/OsEventHandler.h b/include/events/OsEventHandler.h index fef22af0b..e65865f5e 100644 --- a/include/events/OsEventHandler.h +++ b/include/events/OsEventHandler.h @@ -10,6 +10,7 @@ #include #include #include +#include #endif #include @@ -21,8 +22,9 @@ class OsEventHandlerBase : public QObject Q_OBJECT public: + OsEventHandlerBase(); - ~OsEventHandlerBase() override; + virtual ~OsEventHandlerBase(); public slots: void suspend(bool sleep); @@ -46,17 +48,20 @@ public slots: bool _isSuspendRegistered; bool _isLockRegistered; - Logger * _log {}; + bool _isService; + + Logger* _log{}; }; #if defined(_WIN32) class OsEventHandlerWindows : public OsEventHandlerBase, public QAbstractNativeEventFilter { - public: OsEventHandlerWindows(); - ~OsEventHandlerWindows() override; + ~OsEventHandlerWindows(); + + void handleSuspendResumeEvent(bool sleep); protected: #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -65,13 +70,17 @@ class OsEventHandlerWindows : public OsEventHandlerBase, public QAbstractNativeE bool nativeEventFilter(const QByteArray& eventType, void* message, long int* result) override; #endif -private: bool registerOsEventHandler() override; void unregisterOsEventHandler() override; bool registerLockHandler() override; void unregisterLockHandler() override; - QWidget _widget; +private: + static OsEventHandlerWindows* getInstance(); + + static DEVICE_NOTIFY_CALLBACK_ROUTINE handlePowerNotifications; + + QWidget* _widget; HPOWERNOTIFY _notifyHandle; }; @@ -82,7 +91,7 @@ class OsEventHandlerLinux : public OsEventHandlerBase { Q_OBJECT - static void static_signaleHandler(int signum) + static void static_signaleHandler(int signum) { OsEventHandlerLinux::getInstance()->handleSignal(signum); } @@ -90,7 +99,7 @@ class OsEventHandlerLinux : public OsEventHandlerBase public: OsEventHandlerLinux(); - void handleSignal (int signum); + void handleSignal(int signum); private: static OsEventHandlerLinux* getInstance(); @@ -119,8 +128,8 @@ class OsEventHandlerMacOS : public OsEventHandlerBase bool registerLockHandler() override; void unregisterLockHandler() override; - void *_sleepEventHandler; - void *_lockEventHandler; + void* _sleepEventHandler; + void* _lockEventHandler; }; using OsEventHandler = OsEventHandlerMacOS; diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 979a0bf48..866e262d8 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include // hyperion includes #include @@ -389,6 +391,9 @@ void JsonAPI::handleSysInfoCommand(const QJsonObject &, const QString &command, hyperion["rootPath"] = _instanceManager->getRootPath(); hyperion["readOnlyMode"] = _hyperion->getReadOnlyMode(); + QCoreApplication* app = QCoreApplication::instance(); + hyperion["isGuiMode"] = qobject_cast(app) ? true : false; + info["hyperion"] = hyperion; // send the result diff --git a/libsrc/events/OsEventHandler.cpp b/libsrc/events/OsEventHandler.cpp index e0684b95f..501e213dc 100644 --- a/libsrc/events/OsEventHandler.cpp +++ b/libsrc/events/OsEventHandler.cpp @@ -3,19 +3,22 @@ #include #include #include +#include +#include #include #include -#include - #if defined(_WIN32) -#include + #include #include #include +#include #pragma comment( lib, "wtsapi32.lib" ) +#pragma comment(lib, "PowrProf.lib") + #elif defined(__APPLE__) #include #endif @@ -26,10 +29,16 @@ OsEventHandlerBase::OsEventHandlerBase() , _isSuspendOnLock(false) , _isSuspendRegistered(false) , _isLockRegistered(false) + , _isService(false) { qRegisterMetaType("Event"); _log = Logger::getInstance("EVENTS-OS"); + QCoreApplication* app = QCoreApplication::instance(); + if (!qobject_cast(app)) + { + _isService = true; + } QObject::connect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); } @@ -45,7 +54,7 @@ OsEventHandlerBase::~OsEventHandlerBase() void OsEventHandlerBase::handleSettingsUpdate(settings::type type, const QJsonDocument& config) { - if(type == settings::OSEVENTS) + if (type == settings::OSEVENTS) { const QJsonObject& obj = config.object(); @@ -65,15 +74,18 @@ void OsEventHandlerBase::handleSettingsUpdate(settings::type type, const QJsonDo unregisterOsEventHandler(); } - _isLockEnabled = obj["lockEnable"].toBool(true); - if (_isLockEnabled || _isSuspendOnLock != prevIsSuspendOnLock) - { - // Listen to lock/screensaver events received by the OS - registerLockHandler(); - } - else + if (!_isService) { - unregisterLockHandler(); + _isLockEnabled = obj["lockEnable"].toBool(true); + if (_isLockEnabled || _isSuspendOnLock != prevIsSuspendOnLock) + { + // Listen to lock/screensaver events received by the OS + registerLockHandler(); + } + else + { + unregisterLockHandler(); + } } } } @@ -118,8 +130,15 @@ void OsEventHandlerBase::lock(bool isLocked) #if defined(_WIN32) +OsEventHandlerWindows* OsEventHandlerWindows::getInstance() +{ + static OsEventHandlerWindows instance; + return &instance; +} + OsEventHandlerWindows::OsEventHandlerWindows() - : _notifyHandle(NULL) + : _notifyHandle(NULL), + _widget(nullptr) { } @@ -127,6 +146,8 @@ OsEventHandlerWindows::~OsEventHandlerWindows() { unregisterLockHandler(); unregisterOsEventHandler(); + + delete _widget; } #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) @@ -135,8 +156,8 @@ bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* message, long int* /*result*/) #endif { - MSG* msg = static_cast(message); + MSG* msg = static_cast(message); switch (msg->message) { case WM_WTSSESSION_CHANGE: @@ -152,22 +173,34 @@ bool OsEventHandlerWindows::nativeEventFilter(const QByteArray& eventType, void* break; } break; + } + return false; +} - case WM_POWERBROADCAST: - switch (msg->wParam) - { - case PBT_APMRESUMESUSPEND: - emit suspend(false); - return true; - break; - case PBT_APMSUSPEND: - emit suspend(true); - return true; - break; - } +void OsEventHandlerWindows::handleSuspendResumeEvent(bool sleep) +{ + if (sleep) + { + suspend(true); + } + else + { + suspend(false); + } +} + +ULONG OsEventHandlerWindows::handlePowerNotifications(PVOID Context, ULONG Type, PVOID Setting) +{ + switch (Type) + { + case PBT_APMRESUMESUSPEND: + getInstance()->handleSuspendResumeEvent(false); + break; + case PBT_APMSUSPEND: + getInstance()->handleSuspendResumeEvent(true); break; } - return false; + return S_OK; } bool OsEventHandlerWindows::registerOsEventHandler() @@ -175,11 +208,10 @@ bool OsEventHandlerWindows::registerOsEventHandler() bool isRegistered{ _isSuspendRegistered }; if (!_isSuspendRegistered) { - auto handle = reinterpret_cast (_widget.winId()); - _notifyHandle = RegisterSuspendResumeNotification(handle, DEVICE_NOTIFY_WINDOW_HANDLE); - if (_notifyHandle != NULL) + DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS notificationsParameters = { handlePowerNotifications, NULL }; + if (PowerRegisterSuspendResumeNotification(DEVICE_NOTIFY_CALLBACK, (HANDLE)¬ificationsParameters, &_notifyHandle) == ERROR_SUCCESS) { - QCoreApplication::instance()->installNativeEventFilter(this); + isRegistered = true; } else { @@ -200,8 +232,7 @@ void OsEventHandlerWindows::unregisterOsEventHandler() { if (_notifyHandle != NULL) { - QCoreApplication::instance()->removeNativeEventFilter(this); - UnregisterSuspendResumeNotification(_notifyHandle); + PowerUnregisterSuspendResumeNotification(_notifyHandle); } _notifyHandle = NULL; _isSuspendRegistered = false; @@ -213,17 +244,23 @@ bool OsEventHandlerWindows::registerLockHandler() bool isRegistered{ _isLockRegistered }; if (!_isLockRegistered) { - auto handle = reinterpret_cast (_widget.winId()); - if (WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION)) + if (!_isService) { - isRegistered = true; - } - else - { - Error(_log, "Could not register for lock/unlock events!"); + _widget = new QWidget(); + if (_widget) + { + auto handle = reinterpret_cast (_widget->winId()); + if (WTSRegisterSessionNotification(handle, NOTIFY_FOR_THIS_SESSION)) + { + QCoreApplication::instance()->installNativeEventFilter(this); + isRegistered = true; + } + else + { + Error(_log, "Could not register for lock/unlock events!"); + } + } } - - } if (isRegistered) @@ -237,9 +274,13 @@ void OsEventHandlerWindows::unregisterLockHandler() { if (_isLockRegistered) { - auto handle = reinterpret_cast (_widget.winId()); - WTSUnRegisterSessionNotification(handle); - _isLockRegistered = false; + if (!_isService) + { + auto handle = reinterpret_cast (_widget->winId()); + QCoreApplication::instance()->removeNativeEventFilter(this); + WTSUnRegisterSessionNotification(handle); + _isLockRegistered = false; + } } } @@ -259,7 +300,7 @@ OsEventHandlerLinux::OsEventHandlerLinux() signal(SIGUSR2, static_signaleHandler); } -void OsEventHandlerLinux::handleSignal (int signum) +void OsEventHandlerLinux::handleSignal(int signum) { if (signum == SIGUSR1) { @@ -286,20 +327,20 @@ typedef QMultiMap DbusSignalsMap; // Constants namespace { -const DbusSignalsMap dbusSignals = { - //system signals - {"Suspend", {"org.freedesktop.login1","/org/freedesktop/login1","org.freedesktop.login1.Manager","PrepareForSleep"}}, - - //Session signals - {"ScreenSaver", {"org.freedesktop.ScreenSaver","/org/freedesktop/ScreenSaver","org.freedesktop.ScreenSaver","ActiveChanged"}}, - {"ScreenSaver", {"org.gnome.ScreenSaver","/org/gnome/ScreenSaver","org.gnome.ScreenSaver","ActiveChanged"}}, -}; + 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}; + bool isRegistered{ _isSuspendRegistered }; if (!_isSuspendRegistered) { QDBusConnection systemBus = QDBusConnection::systemBus(); @@ -311,10 +352,10 @@ bool OsEventHandlerLinux::registerOsEventHandler() { 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)))) + 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; @@ -347,10 +388,10 @@ void OsEventHandlerLinux::unregisterOsEventHandler() { 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)))) + 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; @@ -365,7 +406,7 @@ void OsEventHandlerLinux::unregisterOsEventHandler() bool OsEventHandlerLinux::registerLockHandler() { - bool isRegistered {_isLockRegistered}; + bool isRegistered{ _isLockRegistered }; if (!_isLockRegistered) { @@ -380,10 +421,10 @@ bool OsEventHandlerLinux::registerLockHandler() 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)))) + 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; @@ -409,7 +450,7 @@ bool OsEventHandlerLinux::registerLockHandler() void OsEventHandlerLinux::unregisterLockHandler() { - bool isUnregistered {false}; + bool isUnregistered{ false }; if (_isLockRegistered) { @@ -424,10 +465,10 @@ void OsEventHandlerLinux::unregisterLockHandler() 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)))) + 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; @@ -459,52 +500,52 @@ OsEventHandlerMacOS::OsEventHandlerMacOS() @interface SleepEvents : NSObject { - OsEventHandlerMacOS *_eventHandler; + OsEventHandlerMacOS* _eventHandler; } -- (id)initSleepEvents:(OsEventHandlerMacOS *)osEventHandler; +- (id)initSleepEvents : (OsEventHandlerMacOS*)osEventHandler; @end @implementation SleepEvents -- (id)initSleepEvents:(OsEventHandlerMacOS *)osEventHandler +- (id)initSleepEvents:(OsEventHandlerMacOS*)osEventHandler { - if ((self = [super init])) + 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]; - } + _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; + return self; } - (void)dealloc { - [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; + [[[NSWorkspace sharedWorkspace]notificationCenter] removeObserver:self]; _eventHandler = nullptr; - [super dealloc]; + [super dealloc] ; } -- (void) receiveSleepWake:(NSNotification*)notification +- (void)receiveSleepWake:(NSNotification*)notification { if (!_eventHandler) return; - if (notification.name == NSWorkspaceWillSleepNotification) + if (notification.name == NSWorkspaceWillSleepNotification) { _eventHandler->suspend(true); } - else if (notification.name == NSWorkspaceDidWakeNotification) - { + else if (notification.name == NSWorkspaceDidWakeNotification) + { _eventHandler->suspend(false); - } + } } @end bool OsEventHandlerMacOS::registerOsEventHandler() { - bool isRegistered {_isSuspendRegistered}; + bool isRegistered{ _isSuspendRegistered }; if (!_isSuspendRegistered) { - _sleepEventHandler = [[SleepEvents alloc] initSleepEvents:this]; + _sleepEventHandler = [[SleepEvents alloc]initSleepEvents:this]; if (_sleepEventHandler) { Debug(_log, "Registered for suspend/resume events"); @@ -527,7 +568,7 @@ void OsEventHandlerMacOS::unregisterOsEventHandler() { if (_isSuspendRegistered && _sleepEventHandler) { - [(SleepEvents *)_sleepEventHandler release], _sleepEventHandler = nil; + [(SleepEvents*)_sleepEventHandler release] , _sleepEventHandler = nil; if (!_sleepEventHandler) { Debug(_log, "Unregistered for suspend/resume events"); @@ -542,43 +583,43 @@ void OsEventHandlerMacOS::unregisterOsEventHandler() @interface LockEvents : NSObject { - OsEventHandlerMacOS *_eventHandler; + OsEventHandlerMacOS* _eventHandler; } -- (id)initLockEvents:(OsEventHandlerMacOS *)osEventHandler; +- (id)initLockEvents : (OsEventHandlerMacOS*)osEventHandler; @end @implementation LockEvents -- (id)initLockEvents:(OsEventHandlerMacOS *)osEventHandler +- (id)initLockEvents:(OsEventHandlerMacOS*)osEventHandler { - if ((self = [super init])) + if ((self = [super init])) { - _eventHandler = osEventHandler; + _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]; - } + [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; + return self; } - (void)dealloc { - [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self]; + [[[NSWorkspace sharedWorkspace]notificationCenter] removeObserver:self]; _eventHandler = nullptr; - [super dealloc]; + [super dealloc] ; } -- (void) receiveLockUnlock:(NSNotification*)notification +- (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"))) - { + else if (CFEqual(notification.name, CFSTR("com.apple.screenIsUnlocked"))) + { _eventHandler->lock(false); - } + } } @end @@ -587,7 +628,7 @@ bool OsEventHandlerMacOS::registerLockHandler() bool isRegistered{ _isLockRegistered }; if (!_isLockRegistered) { - _lockEventHandler = [[LockEvents alloc] initLockEvents:this]; + _lockEventHandler = [[LockEvents alloc]initLockEvents:this]; if (_lockEventHandler) { Debug(_log, "Registered for lock/unlock events"); @@ -610,7 +651,7 @@ void OsEventHandlerMacOS::unregisterLockHandler() { if (_isLockRegistered && _lockEventHandler) { - [(LockEvents *)_lockEventHandler release], _lockEventHandler = nil; + [(LockEvents*)_lockEventHandler release] , _lockEventHandler = nil; if (!_lockEventHandler) { Debug(_log, "Unregistered for lock/unlock events");