kde-workspace/powerdevil/daemon/powerdevilcore.cpp
2014-11-13 19:30:51 +02:00

730 lines
29 KiB
C++

/***************************************************************************
* Copyright (C) 2010 by Dario Freddi <drf@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#include "powerdevilcore.h"
#include "PowerDevilSettings.h"
#include "screensaver_interface.h"
#include "powerdevilaction.h"
#include "powerdevilactionpool.h"
#include "powerdevilbackendinterface.h"
#include "powerdevilpolicyagent.h"
#include "powerdevilprofilegenerator.h"
#include "daemon/actions/bundled/suspendsession.h"
#include <Solid/Battery>
#include <Solid/Device>
#include <Solid/DeviceNotifier>
#include <KDebug>
#include <KIdleTime>
#include <KLocalizedString>
#include <KMessageBox>
#include <KNotification>
#include <KServiceTypeTrader>
#include <KStandardDirs>
#include <KActivities/Consumer>
#include <QtCore/QTimer>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusConnectionInterface>
namespace PowerDevil
{
Core::Core(QObject* parent, const KComponentData &componentData)
: QObject(parent)
, m_backend(0)
, m_applicationData(componentData)
, m_criticalBatteryTimer(new QTimer(this))
, m_activityConsumer(new KActivities::Consumer(this))
, m_pendingWakeupEvent(true)
{
}
Core::~Core()
{
// Unload all actions before exiting, and clear the cache
ActionPool::instance()->unloadAllActiveActions();
ActionPool::instance()->clearCache();
}
void Core::loadCore(BackendInterface* backend)
{
if (!backend) {
onBackendError(i18n("No valid Power Management backend plugins are available. "
"A new installation might solve this problem."));
return;
}
m_backend = backend;
// Async backend init - so that KDED gets a bit of a speed up
kDebug() << "Core loaded, initializing backend";
connect(m_backend, SIGNAL(backendReady()), this, SLOT(onBackendReady()));
connect(m_backend, SIGNAL(backendError(QString)), this, SLOT(onBackendError(QString)));
m_backend->init();
// Register DBus Metatypes
qDBusRegisterMetaType< StringStringMap >();
}
void Core::onBackendReady()
{
kDebug() << "Backend is ready, KDE Power Management system initialized";
m_profilesConfig = KSharedConfig::openConfig("powermanagementprofilesrc", KConfig::CascadeConfig);
// Is it brand new?
if (m_profilesConfig->groupList().isEmpty()) {
// Generate defaults
bool toRam = m_backend->supportedSuspendMethods() & PowerDevil::BackendInterface::ToRam;
bool toDisk = m_backend->supportedSuspendMethods() & PowerDevil::BackendInterface::ToDisk;
if (ProfileGenerator::generateProfiles(toRam, toDisk, true) == ProfileGenerator::ResultUpgraded) {
// Notify the user
emitNotification("warningnot", i18n("Your Power Profiles have been updated to be used with the new KDE Power "
"Management System. You can tweak them or generate a new set of defaults from "
"System Settings."), "system-software-update");
}
m_profilesConfig->reparseConfiguration();
}
// Get the battery devices ready
{
using namespace Solid;
connect(DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)),
this, SLOT(onDeviceAdded(QString)));
connect(DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)),
this, SLOT(onDeviceRemoved(QString)));
// Force the addition of already existent batteries
foreach (const Device &device, Device::listFromType(DeviceInterface::Battery, QString())) {
onDeviceAdded(device.udi());
}
}
connect(m_backend, SIGNAL(acAdapterStateChanged(PowerDevil::BackendInterface::AcAdapterState)),
this, SLOT(onAcAdapterStateChanged(PowerDevil::BackendInterface::AcAdapterState)));
connect(m_backend, SIGNAL(batteryRemainingTimeChanged(qulonglong)),
this, SLOT(onBatteryRemainingTimeChanged(qulonglong)));
connect(KIdleTime::instance(), SIGNAL(timeoutReached(int,int)),
this, SLOT(onKIdleTimeoutReached(int,int)));
connect(KIdleTime::instance(), SIGNAL(resumingFromIdle()),
this, SLOT(onResumingFromIdle()));
connect(m_activityConsumer, SIGNAL(currentActivityChanged(QString)),
this, SLOT(loadProfile()));
// Set up the policy agent
PowerDevil::PolicyAgent::instance()->init();
// Initialize the action pool, which will also load the needed startup actions.
PowerDevil::ActionPool::instance()->init(this);
// Set up the critical battery timer
m_criticalBatteryTimer->setSingleShot(true);
m_criticalBatteryTimer->setInterval(30000);
connect(m_criticalBatteryTimer, SIGNAL(timeout()), this, SLOT(onCriticalBatteryTimerExpired()));
// In 30 seconds (so we are sure the user sees eventual notifications), check the battery state
QTimer::singleShot(30000, this, SLOT(checkBatteryStatus()));
// All systems up Houston, let's go!
emit coreReady();
refreshStatus();
}
bool Core::isActionSupported(const QString& actionName)
{
Action *action = ActionPool::instance()->loadAction(actionName, KConfigGroup(), this);
if (!action) {
return false;
} else {
return action->isSupported();
}
}
QString Core::checkBatteryStatus(bool notify)
{
QString lastMessage;
// Any batteries below 50% of capacity?
for (QHash< QString, uint >::const_iterator i = m_backend->capacities().constBegin();
i != m_backend->capacities().constEnd(); ++i) {
if (i.value() < 50) {
// Notify, we have a broken battery.
if (m_loadedBatteriesUdi.size() == 1) {
lastMessage = i18n("Your battery capacity is %1%. This means your battery is broken and "
"needs a replacement. Please contact your hardware vendor for more details.",
i.value());
} else {
lastMessage = i18n("One of your batteries (ID %2) has a capacity of %1%. This means it is broken "
"and needs a replacement. Please contact your hardware vendor for more details.",
i.value(), i.key());
}
if (notify) {
emitRichNotification("brokenbattery", i18n("Broken Battery"), lastMessage);
}
}
}
// Any recalled batteries?
foreach (const BackendInterface::RecallNotice &notice, m_backend->recallNotices()) {
// Notify, a battery has been recalled
if (m_loadedBatteriesUdi.size() == 1) {
lastMessage = i18n("Your battery might have been recalled by %1. Usually, when vendors recall the "
"hardware, it is because of factory defects which are usually eligible for a "
"free repair or substitution. Please check <a href=\"%2\">%1's website</a> to "
"verify if your battery is faulted.", notice.vendor, notice.url);
} else {
lastMessage = i18n("One of your batteries (ID %3) might have been recalled by %1. "
"Usually, when vendors recall the hardware, it is because of factory defects "
"which are usually eligible for a free repair or substitution. "
"Please check <a href=\"%2\">%1's website</a> to "
"verify if your battery is faulted.", notice.vendor, notice.url, notice.batteryId);
}
if (notify) {
emitRichNotification("brokenbattery", i18n("Check Your Battery"), lastMessage);
}
}
return lastMessage;
}
void Core::refreshStatus()
{
/* The configuration could have changed if this function was called, so
* let's resync it.
*/
reparseConfiguration();
loadProfile(true);
}
void Core::reparseConfiguration()
{
PowerDevilSettings::self()->readConfig();
m_profilesConfig->reparseConfiguration();
// Config reloaded
emit configurationReloaded();
}
void Core::loadProfile(bool force)
{
QString profileId;
// Policy check
if (PolicyAgent::instance()->requirePolicyCheck(PolicyAgent::ChangeProfile) != PolicyAgent::None) {
kDebug() << "Policy Agent prevention: on";
return;
}
KConfigGroup config;
// Check the activity in which we are in
QString activity = m_activityConsumer->currentActivity();
if (activity.isEmpty()) {
activity = "default";
}
kDebug() << "We are now into activity " << activity;
KConfigGroup activitiesConfig(m_profilesConfig, "Activities");
kDebug() << activitiesConfig.groupList() << activitiesConfig.keyList();
// Are we mirroring an activity?
if (activitiesConfig.group(activity).readEntry("mode", "None") == "ActLike" &&
activitiesConfig.group(activity).readEntry("actLike", QString()) != "AC" &&
activitiesConfig.group(activity).readEntry("actLike", QString()) != "Battery" &&
activitiesConfig.group(activity).readEntry("actLike", QString()) != "LowBattery") {
// Yes, let's use that then
activity = activitiesConfig.group(activity).readEntry("actLike", QString());
kDebug() << "Activity is a mirror";
}
KConfigGroup activityConfig = activitiesConfig.group(activity);
kDebug() << activityConfig.groupList() << activityConfig.keyList();
// See if this activity has priority
if (activityConfig.readEntry("mode", "None") == "SeparateSettings") {
// Prioritize this profile over anything
config = activityConfig.group("SeparateSettings");
kDebug() << "Activity is enforcing a different profile";
profileId = activity;
} else if (activityConfig.readEntry("mode", "None") == "ActLike") {
if (activityConfig.readEntry("actLike", QString()) == "AC" ||
activityConfig.readEntry("actLike", QString()) == "Battery" ||
activityConfig.readEntry("actLike", QString()) == "LowBattery") {
// Same as above, but with an existing profile
config = m_profilesConfig.data()->group(activityConfig.readEntry("actLike", QString()));
profileId = activityConfig.readEntry("actLike", QString());
kDebug() << "Activity is mirroring a different profile";
}
} else {
// It doesn't, let's load the current state's profile
if (m_loadedBatteriesUdi.isEmpty()) {
kDebug() << "No batteries found, loading AC";
profileId = "AC";
} else {
// Compute the previous and current global percentage
int percent = 0;
for (QHash<QString,int>::const_iterator i = m_batteriesPercent.constBegin();
i != m_batteriesPercent.constEnd(); ++i) {
percent += i.value();
}
if (backend()->acAdapterState() == BackendInterface::Plugged) {
profileId = "AC";
kDebug() << "Loading profile for plugged AC";
} else if (percent <= PowerDevilSettings::batteryLowLevel()) {
profileId = "LowBattery";
kDebug() << "Loading profile for low battery";
} else {
profileId = "Battery";
kDebug() << "Loading profile for unplugged AC";
}
}
config = m_profilesConfig.data()->group(profileId);
kDebug() << "Activity is not forcing a profile";
}
// Release any special inhibitions
{
QHash<QString,int>::iterator i = m_sessionActivityInhibit.begin();
while (i != m_sessionActivityInhibit.end()) {
PolicyAgent::instance()->ReleaseInhibition(i.value());
i = m_sessionActivityInhibit.erase(i);
}
i = m_screenActivityInhibit.begin();
while (i != m_screenActivityInhibit.end()) {
PolicyAgent::instance()->ReleaseInhibition(i.value());
i = m_screenActivityInhibit.erase(i);
}
}
if (!config.isValid()) {
emitNotification("powerdevilerror", i18n("The profile \"%1\" has been selected, "
"but it does not exist.\nPlease check your PowerDevil configuration.",
profileId));
return;
}
// Check: do we need to change profile at all?
if (m_currentProfile == profileId && !force) {
// No, let's leave things as they are
kDebug() << "Skipping action reload routine as profile has not changed";
// Do we need to force a wakeup?
if (m_pendingWakeupEvent) {
// Fake activity at this stage, when no timeouts are registered
onResumingFromIdle();
KIdleTime::instance()->simulateUserActivity();
m_pendingWakeupEvent = false;
}
} else {
// First of all, let's clean the old actions. This will also call the onProfileUnload callback
ActionPool::instance()->unloadAllActiveActions();
// Do we need to force a wakeup?
if (m_pendingWakeupEvent) {
// Fake activity at this stage, when no timeouts are registered
onResumingFromIdle();
KIdleTime::instance()->simulateUserActivity();
m_pendingWakeupEvent = false;
}
// Cool, now let's load the needed actions
foreach (const QString &actionName, config.groupList()) {
Action *action = ActionPool::instance()->loadAction(actionName, config.group(actionName), this);
if (action) {
action->onProfileLoad();
} else {
// Ouch, error. But let's just warn and move on anyway
//TODO Maybe Remove from the configuration if unsupported
kWarning() << "The profile " << profileId << "tried to activate"
<< actionName << "a non existent action. This is usually due to an installation problem"
" or to a configuration problem. or simlpy the action is not supported";
}
}
// We are now on a different profile
m_currentProfile = profileId;
emit profileChanged(m_currentProfile);
}
// Now... any special behaviors we'd like to consider?
if (activityConfig.readEntry("mode", "None") == "SpecialBehavior") {
kDebug() << "Activity has special behaviors";
KConfigGroup behaviorGroup = activityConfig.group("SpecialBehavior");
if (behaviorGroup.readEntry("performAction", false)) {
// Let's override the configuration for this action at all times
ActionPool::instance()->loadAction("SuspendSession", behaviorGroup.group("ActionConfig"), this);
kDebug() << "Activity overrides suspend session action";
}
if (behaviorGroup.readEntry("noSuspend", false)) {
kDebug() << "Activity triggers a suspend inhibition";
// Trigger a special inhibition - if we don't have one yet
if (!m_sessionActivityInhibit.contains(activity)) {
int cookie =
PolicyAgent::instance()->AddInhibition(PolicyAgent::InterruptSession, i18n("Activity Manager"),
i18n("This activity's policies prevent the system from suspending"));
m_sessionActivityInhibit.insert(activity, cookie);
}
}
if (behaviorGroup.readEntry("noScreenManagement", false)) {
kDebug() << "Activity triggers a screen management inhibition";
// Trigger a special inhibition - if we don't have one yet
if (!m_screenActivityInhibit.contains(activity)) {
int cookie =
PolicyAgent::instance()->AddInhibition(PolicyAgent::ChangeScreenSettings, i18n("Activity Manager"),
i18n("This activity's policies prevent screen power management"));
m_screenActivityInhibit.insert(activity, cookie);
}
}
}
// If the lid is closed, retrigger the lid close signal
if (m_backend->isLidClosed()) {
emit m_backend->buttonPressed(PowerDevil::BackendInterface::LidClose);
}
}
void Core::onDeviceAdded(const QString& udi)
{
if (m_loadedBatteriesUdi.contains(udi)) {
// We already know about this device
return;
}
using namespace Solid;
Device device(udi);
Battery *b = qobject_cast<Battery*>(device.asDeviceInterface(DeviceInterface::Battery));
if (!b) {
// Not interesting to us
return;
}
if (b->type() != Solid::Battery::PrimaryBattery && b->type() != Solid::Battery::UpsBattery) {
// Not interesting to us
return;
}
if (!b->isPowerSupply()) {
// TODO: At some later point it would be really nice to handle those batteries too
// eg, show "your mouse is running low", but in the mean time, we don't care about those
return;
}
if (!connect(b, SIGNAL(chargePercentChanged(int,QString)),
this, SLOT(onBatteryChargePercentChanged(int,QString))) ||
!connect(b, SIGNAL(chargeStateChanged(int,QString)),
this, SLOT(onBatteryChargeStateChanged(int,QString)))) {
emitNotification("powerdevilerror", i18n("Could not connect to battery interface.\n"
"Please check your system configuration"));
}
kDebug() << "A new battery was detected";
m_batteriesPercent[udi] = b->chargePercent();
if (b->chargeState() == Solid::Battery::NoCharge) {
m_batteriesCharged[udi] = true;
} else {
m_batteriesCharged[udi] = false;
}
m_loadedBatteriesUdi.append(udi);
// Compute the current global percentage
int globalChargePercent = 0;
for (QHash<QString,int>::const_iterator i = m_batteriesPercent.constBegin(); i != m_batteriesPercent.constEnd(); ++i) {
globalChargePercent += i.value();
}
// If a new battery has been added, let's clear some pending suspend actions if the new global batteries percentage is
// higher than the battery low level. (See bug 329537)
if (m_criticalBatteryTimer->isActive() && globalChargePercent > PowerDevilSettings::batteryCriticalLevel()) {
m_criticalBatteryTimer->stop();
}
// So we get a "Battery is low" notification directly on system startup if applicable
emitBatteryChargePercentNotification(globalChargePercent, 100);
}
void Core::onDeviceRemoved(const QString& udi)
{
if (!m_loadedBatteriesUdi.contains(udi)) {
// We don't know about this device
return;
}
using namespace Solid;
Device device(udi);
Battery *b = qobject_cast<Battery*>(device.asDeviceInterface(DeviceInterface::Battery));
disconnect(b, SIGNAL(chargePercentChanged(int,QString)),
this, SLOT(onBatteryChargePercentChanged(int,QString)));
disconnect(b, SIGNAL(chargeStateChanged(int,QString)),
this, SLOT(onBatteryChargeStateChanged(int,QString)));
kDebug() << "An existing battery has been removed";
m_batteriesPercent.remove(udi);
m_batteriesCharged.remove(udi);
m_loadedBatteriesUdi.removeOne(udi);
}
void Core::emitNotification(const QString &evid, const QString &message, const QString &iconname)
{
if (!iconname.isEmpty()) {
KNotification::event(evid, message, KIcon(iconname).pixmap(48,48),
0, KNotification::CloseOnTimeout, m_applicationData);
} else {
KNotification::event(evid, message, QPixmap(),
0, KNotification::CloseOnTimeout, m_applicationData);
}
}
void Core::emitRichNotification(const QString &evid, const QString &title, const QString &message)
{
KNotification::event(evid, title, message, QPixmap(),
0, KNotification::CloseOnTimeout, m_applicationData);
}
bool Core::emitBatteryChargePercentNotification(int currentPercent, int previousPercent)
{
if (m_backend->acAdapterState() == BackendInterface::Plugged) {
return false;
}
if (currentPercent <= PowerDevilSettings::batteryCriticalLevel() &&
previousPercent > PowerDevilSettings::batteryCriticalLevel()) {
switch (PowerDevilSettings::batteryCriticalAction()) {
case PowerDevil::BundledActions::SuspendSession::ShutdownMode:
emitRichNotification("criticalbattery", i18n("Battery Critical (%1% Remaining)", currentPercent),
i18n("Your battery level is critical, the computer will be halted in 30 seconds."));
m_criticalBatteryTimer->start();
break;
case PowerDevil::BundledActions::SuspendSession::ToDiskMode:
emitRichNotification("criticalbattery", i18n("Battery Critical (%1% Remaining)", currentPercent),
i18n("Your battery level is critical, the computer will be hibernated in 30 seconds."));
m_criticalBatteryTimer->start();
break;
case PowerDevil::BundledActions::SuspendSession::ToRamMode:
emitRichNotification("criticalbattery", i18n("Battery Critical (%1% Remaining)", currentPercent),
i18n("Your battery level is critical, the computer will be suspended in 30 seconds."));
m_criticalBatteryTimer->start();
break;
default:
emitRichNotification("criticalbattery", i18n("Battery Critical (%1% Remaining)", currentPercent),
i18n("Your battery level is critical, save your work as soon as possible."));
break;
}
return true;
} else if (currentPercent <= PowerDevilSettings::batteryLowLevel() &&
previousPercent > PowerDevilSettings::batteryLowLevel()) {
emitRichNotification("lowbattery", i18n("Battery Low (%1% Remaining)", currentPercent),
i18n("Your battery is low. If you need to continue using your computer, either plug in your computer, or shut it down and then change the battery."));
return true;
}
return false;
}
void Core::onAcAdapterStateChanged(PowerDevil::BackendInterface::AcAdapterState state)
{
kDebug();
// Post request for faking an activity event - usually adapters don't plug themselves out :)
m_pendingWakeupEvent = true;
loadProfile();
if (state == BackendInterface::Plugged) {
// If the AC Adaptor has been plugged in, let's clear some pending suspend actions
if (m_criticalBatteryTimer->isActive()) {
m_criticalBatteryTimer->stop();
emitRichNotification("criticalbattery",
i18n("AC Adapter Plugged In"),
i18n("All pending suspend actions have been canceled."));
} else {
emitRichNotification("pluggedin", i18n("Running on AC power"), i18n("The power adaptor has been plugged in."));
}
} else if (state == BackendInterface::Unplugged) {
emitRichNotification("unplugged", i18n("Running on Battery Power"), i18n("The power adaptor has been unplugged."));
}
}
void Core::onBackendError(const QString& error)
{
emitNotification("powerdevilerror", i18n("KDE Power Management System could not be initialized. "
"The backend reported the following error: %1\n"
"Please check your system configuration", error));
}
void Core::onBatteryChargePercentChanged(int percent, const QString &udi)
{
// Compute the previous and current global percentage
int previousPercent = 0;
for (QHash<QString,int>::const_iterator i = m_batteriesPercent.constBegin(); i != m_batteriesPercent.constEnd(); ++i) {
previousPercent += i.value();
}
int currentPercent = previousPercent - (m_batteriesPercent[udi] - percent);
// Update the battery percentage
m_batteriesPercent[udi] = percent;
if (currentPercent < previousPercent) {
if (emitBatteryChargePercentNotification(currentPercent, previousPercent)) {
// Only refresh status if a notification has actually been emitted
refreshStatus();
}
}
}
void Core::onBatteryChargeStateChanged(int state, const QString &udi)
{
bool previousCharged = true;
for (QHash<QString,bool>::const_iterator i = m_batteriesCharged.constBegin(); i != m_batteriesCharged.constEnd(); ++i) {
if (!i.value()) {
previousCharged = false;
break;
}
}
m_batteriesCharged[udi] = (state == Solid::Battery::NoCharge);
if (m_backend->acAdapterState() != BackendInterface::Plugged) {
return;
}
bool currentCharged = true;
for (QHash<QString,bool>::const_iterator i = m_batteriesCharged.constBegin(); i != m_batteriesCharged.constEnd(); ++i) {
if (!i.value()) {
currentCharged = false;
break;
}
}
if (!previousCharged && currentCharged) {
emitRichNotification("fullbattery", i18n("Charge Complete"), i18n("Your battery is now fully charged."));
refreshStatus();
}
}
void Core::onCriticalBatteryTimerExpired()
{
// Do that only if we're not on AC
if (m_backend->acAdapterState() == BackendInterface::Unplugged) {
// We consider this as a very special button
PowerDevil::Action *helperAction = ActionPool::instance()->loadAction("HandleButtonEvents", KConfigGroup(), this);
if (helperAction) {
QVariantMap args;
args["Button"] = 32;
args["Type"] = QVariant::fromValue<uint>(PowerDevilSettings::batteryCriticalAction());
helperAction->trigger(args);
}
}
}
void Core::onBatteryRemainingTimeChanged(qulonglong time)
{
emit batteryRemainingTimeChanged(time);
}
void Core::onKIdleTimeoutReached(int identifier, int msec)
{
// Find which action(s) requested this idle timeout
for (QHash< Action*, QList< int > >::const_iterator i = m_registeredActionTimeouts.constBegin();
i != m_registeredActionTimeouts.constEnd(); ++i) {
if (i.value().contains(identifier)) {
i.key()->onIdleTimeout(msec);
// And it will need to be awaken
if (!m_pendingResumeFromIdleActions.contains(i.key())) {
m_pendingResumeFromIdleActions.append(i.key());
}
break;
}
}
// Catch the next resume event if some actions require it
if (!m_pendingResumeFromIdleActions.isEmpty()) {
KIdleTime::instance()->catchNextResumeEvent();
}
}
void Core::registerActionTimeout(Action* action, int timeout)
{
// Register the timeout with KIdleTime
int identifier = KIdleTime::instance()->addIdleTimeout(timeout);
// Add the identifier to the action hash
QList< int > timeouts = m_registeredActionTimeouts[action];
timeouts.append(identifier);
m_registeredActionTimeouts[action] = timeouts;
}
void Core::unregisterActionTimeouts(Action* action)
{
// Clear all timeouts from the action
QList< int > timeoutsToClean = m_registeredActionTimeouts[action];
foreach (int id, timeoutsToClean) {
KIdleTime::instance()->removeIdleTimeout(id);
}
m_registeredActionTimeouts.remove(action);
}
void Core::onResumingFromIdle()
{
// Wake up the actions in which an idle action was triggered
QList< Action* >::iterator i = m_pendingResumeFromIdleActions.begin();
while (i != m_pendingResumeFromIdleActions.end()) {
(*i)->onWakeupFromIdle();
i = m_pendingResumeFromIdleActions.erase(i);
}
}
BackendInterface* Core::backend()
{
return m_backend;
}
bool Core::isLidClosed()
{
return m_backend->isLidClosed();
}
qulonglong Core::batteryRemainingTime() const
{
return m_backend->batteryRemainingTime();
}
uint Core::backendCapabilities()
{
return m_backend->capabilities();
}
}
#include "powerdevilcore.moc"