mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-24 19:02:51 +00:00
730 lines
29 KiB
C++
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 ¬ice, 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"
|