Support platform independent sleep/wake notifications

This commit is contained in:
Brad Davis 2019-08-24 12:41:18 -07:00 committed by Bradley Austin Davis
parent 03e3f38ff1
commit dfd78a2662
9 changed files with 263 additions and 96 deletions

View file

@ -58,6 +58,7 @@
#include <shared/FileUtils.h>
#include <shared/QtHelpers.h>
#include <shared/PlatformHelper.h>
#include <shared/GlobalAppProperties.h>
#include <StatTracker.h>
#include <Trace.h>
@ -257,10 +258,6 @@ extern "C" {
}
#endif
#ifdef Q_OS_MAC
#include "MacHelper.h"
#endif
#if defined(Q_OS_ANDROID)
#include <android/log.h>
#include "AndroidHelper.h"
@ -552,13 +549,6 @@ public:
return true;
}
if (message->message == WM_POWERBROADCAST) {
if (message->wParam == PBT_APMRESUMEAUTOMATIC) {
qCInfo(interfaceapp) << "Waking up from sleep or hybernation.";
QMetaObject::invokeMethod(DependencyManager::get<NodeList>().data(), "noteAwakening", Qt::QueuedConnection);
}
}
if (message->message == WM_COPYDATA) {
COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam);
QUrl url = QUrl((const char*)(pcds->lpData));
@ -964,9 +954,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<KeyboardScriptingInterface>();
DependencyManager::set<GrabManager>();
DependencyManager::set<AvatarPackager>();
#ifdef Q_OS_MAC
DependencyManager::set<MacHelper>();
#endif
PlatformHelper::setup();
QObject::connect(DependencyManager::get<PlatformHelper>().get(), &PlatformHelper::systemWillWake, [] {
QMetaObject::invokeMethod(DependencyManager::get<NodeList>().data(), "noteAwakening", Qt::QueuedConnection);
});
QString setBookmarkValue = getCmdOption(argc, constArgv, "--setBookmark");
if (!setBookmarkValue.isEmpty()) {
@ -1172,6 +1165,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
deadlockWatchdogThread->setMainThreadID(QThread::currentThreadId());
deadlockWatchdogThread->start();
// Pause the deadlock watchdog when we sleep, or it might
// trigger a false positive when we wake back up
auto platformHelper = DependencyManager::get<PlatformHelper>();
connect(platformHelper.get(), &PlatformHelper::systemWillSleep, [] {
DeadlockWatchdogThread::pause();
});
connect(platformHelper.get(), &PlatformHelper::systemWillWake, [] {
DeadlockWatchdogThread::resume();
});
// Main thread timer to keep the watchdog updated
QTimer* watchdogUpdateTimer = new QTimer(this);
@ -2869,9 +2873,7 @@ Application::~Application() {
_gameWorkload.shutdown();
DependencyManager::destroy<Preferences>();
#ifdef Q_OS_MAC
DependencyManager::destroy<MacHelper>();
#endif
PlatformHelper::shutdown();
_entityClipboard->eraseAllOctreeElements();
_entityClipboard.reset();

View file

@ -1,58 +0,0 @@
//
// MacHelper.h
// interface/src
//
// Created by Howard Stearns
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "InterfaceLogging.h"
#include "MacHelper.h"
#include <NodeList.h>
#ifdef Q_OS_MAC
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
// The type definitions in these variables come from IOKit, which includes a definition of Duration that conflicts with ours.
// So... we include these definitions here rather than in the .h, as the .h is included in Application.cpp which
// uses Duration.
static io_connect_t root_port;
static IONotificationPortRef notifyPortRef;
static io_object_t notifierObject;
static void* refCon;
static void sleepHandler(void* refCon, io_service_t service, natural_t messageType, void* messageArgument) {
if (messageType == kIOMessageSystemHasPoweredOn) {
qCInfo(interfaceapp) << "Waking up from sleep or hybernation.";
QMetaObject::invokeMethod(DependencyManager::get<NodeList>().data(), "noteAwakening", Qt::QueuedConnection);
}
}
#endif
MacHelper::MacHelper() {
#ifdef Q_OS_MAC
root_port = IORegisterForSystemPower(refCon, &notifyPortRef, sleepHandler, &notifierObject);
if (root_port == 0) {
qCWarning(interfaceapp) << "IORegisterForSystemPower failed";
return;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notifyPortRef),
kCFRunLoopCommonModes);
#endif
}
MacHelper::~MacHelper() {
#ifdef Q_OS_MAC
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notifyPortRef),
kCFRunLoopCommonModes);
IODeregisterForSystemPower(&notifierObject);
IOServiceClose(root_port);
IONotificationPortDestroy(notifyPortRef);
#endif
}

View file

@ -1,21 +0,0 @@
//
// MacHelper.h
// interface/src
//
// Created by Howard Stearns
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include "DependencyManager.h"
class MacHelper : public Dependency {
public:
MacHelper();
~MacHelper();
};

View file

@ -0,0 +1,27 @@
//
// Created by Bradley Austin Davis on 2019/08/22
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "PlatformHelper.h"
void PlatformHelper::onSleep() {
if (_awake.exchange(false)) {
qInfo() << "Entering sleep or hibernation.";
emit systemWillSleep();
}
}
void PlatformHelper::onWake() {
if (!_awake.exchange(true)) {
qInfo() << "Waking up from sleep or hibernation.";
emit systemWillWake();
}
}
void PlatformHelper::shutdown() {
DependencyManager::destroy<PlatformHelper>();
}

View file

@ -0,0 +1,41 @@
//
// Created by Bradley Austin Davis on 2019/08/22
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_shared_PlatformHelper_h
#define hifi_shared_PlatformHelper_h
#include <atomic>
#include <QtCore/QtGlobal>
#include "../DependencyManager.h"
class PlatformHelper : public QObject, public Dependency {
Q_OBJECT
public:
PlatformHelper() {}
virtual ~PlatformHelper() {}
void onSleep();
void onWake();
signals:
void systemWillSleep();
void systemWillWake();
public:
// Run the per-platform code to instantiate a platform-dependent PlatformHelper dependency object
static void setup();
// Run the per-platform code to cleanly shutdown a platform-dependent PlatformHelper dependency object
static void shutdown();
std::atomic<bool> _awake{ true };
};
#endif

View file

@ -0,0 +1,27 @@
//
// Created by Bradley Austin Davis on 2019/08/22
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "../PlatformHelper.h"
#if defined(Q_OS_ANDROID)
// FIXME support sleep/wake notifications
class AndroidHelper : public PlatformHelper {
public:
AndroidHelper() {
}
~AndroidHelper() {
}
};
void PlatformHelper::setup() {
DependencyManager::set<PlatformHelper, AndroidHelper>();
}
#endif

View file

@ -0,0 +1,27 @@
//
// Created by Bradley Austin Davis on 2019/08/22
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "../PlatformHelper.h"
#if !defined(Q_OS_ANDROID) && !defined(Q_OS_MAC) && !defined(Q_OS_WIN)
// FIXME support sleep/wake notifications
class LinuxHelper : public PlatformHelper {
public:
LinuxHelper() {
}
~LinuxHelper() {
}
};
void PlatformHelper::setup() {
DependencyManager::set<PlatformHelper, LinuxHelper>();
}
#endif

View file

@ -0,0 +1,70 @@
//
// Created by Bradley Austin Davis on 2019/08/22
// Based on interface/src/MacHelper.cpp, created by Howard Stearns
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "../PlatformHelper.h"
#if !defined(Q_OS_ANDROID) && defined(Q_OS_MAC)
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
class MacHelper : public PlatformHelper {
public:
MacHelper() {
_rootPort = IORegisterForSystemPower(this, &_notifyPortRef, serviceInterestCallback, &_notifierObject);
if (_rootPort == 0) {
qWarning() << "IORegisterForSystemPower failed";
return;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(_notifyPortRef), kCFRunLoopCommonModes);
}
~MacHelper() {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(_notifyPortRef), kCFRunLoopCommonModes);
IODeregisterForSystemPower(&_notifierObject);
IOServiceClose(_rootPort);
IONotificationPortDestroy(_notifyPortRef);
}
private:
void onServiceMessage(io_service_t, natural_t messageType, void* messageArgument) {
switch (messageType) {
case kIOMessageSystemHasPoweredOn:
onWake();
break;
case kIOMessageSystemWillSleep:
onSleep();
// explicit fallthrough
// Per the documentation for kIOMessageSystemWillSleep and kIOMessageCanSystemSleep, the receiver MUST respond
// https://developer.apple.com/documentation/iokit/1557114-ioregisterforsystempower?language=objc
case kIOMessageCanSystemSleep:
IOAllowPowerChange(_rootPort, (long)messageArgument);
break;
default:
break;
}
}
static void serviceInterestCallback(void* refCon, io_service_t service, natural_t messageType, void* messageArgument) {
static_cast<MacHelper*>(refCon)->onServiceMessage(service, messageType, messageArgument);
}
io_connect_t _rootPort{ 0 };
IONotificationPortRef _notifyPortRef{};
io_object_t _notifierObject{};
};
void PlatformHelper::setup() {
DependencyManager::set<PlatformHelper, MacHelper>();
}
#endif

View file

@ -0,0 +1,52 @@
//
// Created by Bradley Austin Davis on 2019/08/22
// Copyright 2013-2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "../PlatformHelper.h"
#if !defined(Q_OS_ANDROID) && defined(Q_OS_WIN)
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QAbstractEventDispatcher>
#include <Windows.h>
class WinHelper : public PlatformHelper, public QAbstractNativeEventFilter {
public:
WinHelper() {
QAbstractEventDispatcher::instance()->installNativeEventFilter(this);
}
~WinHelper() {
auto eventDispatcher = QAbstractEventDispatcher::instance();
if (eventDispatcher) {
eventDispatcher->removeNativeEventFilter(this);
}
}
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override {
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_POWERBROADCAST) {
switch (msg->wParam) {
case PBT_APMRESUMEAUTOMATIC:
case PBT_APMRESUMESUSPEND:
onWake();
break;
case PBT_APMSUSPEND:
onSleep();
break;
}
}
return false;
}
};
void PlatformHelper::setup() {
DependencyManager::set<PlatformHelper, WinHelper>();
}
#endif