1 /***************************************************************************
2 * Copyright (C) 2008 by Dario Freddi <drf@kdemod.ath.cx> *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
18 ***************************************************************************
20 * Part of the code in this file was taken from KDE4Powersave and/or *
21 * Lithium, where noticed. *
23 **************************************************************************/
25 #include "PowerDevilDaemon.h"
27 #include <kdemacros.h>
29 #include <KPluginFactory>
30 #include <KNotification>
32 #include <KMessageBox>
33 #include <kpluginfactory.h>
34 #include <klocalizedstring.h>
36 #include <kworkspace/kworkspace.h>
37 #include <KApplication>
42 #include "PowerDevilSettings.h"
43 #include "powerdeviladaptor.h"
44 #include "PowerManagementConnector.h"
45 #include "PollSystemLoader.h"
46 #include "SuspensionLockHandler.h"
48 #include <solid/device.h>
49 #include <solid/deviceinterface.h>
50 #include <solid/processor.h>
52 #include "screensaver_interface.h"
53 #include "kscreensaver_interface.h"
54 #include "ksmserver_interface.h"
56 #include <config-powerdevil.h>
57 #include <config-workspace.h>
63 #include <X11/Xatom.h>
64 #include <X11/Xutil.h>
68 #include <X11/extensions/dpms.h>
69 int __kde_do_not_unload
= 1;
71 #ifndef HAVE_DPMSCAPABLE_PROTO
72 Bool
DPMSCapable(Display
*);
75 #ifndef HAVE_DPMSINFO_PROTO
76 Status
DPMSInfo(Display
*, CARD16
*, BOOL
*);
80 static XErrorHandler defaultHandler
;
84 #define POLLER_CALL(Object, Method) \
86 AbstractSystemPoller *t = qobject_cast<AbstractSystemPoller *>(Object); \
91 kWarning() << "WARNING: No poller system loaded, PowerDevil can not detect idle time"; \
94 K_PLUGIN_FACTORY(PowerDevilFactory
,
95 registerPlugin
<PowerDevilDaemon
>();)
96 K_EXPORT_PLUGIN(PowerDevilFactory("powerdevildaemon"))
98 class PowerDevilDaemon::Private
102 : notifier(Solid::Control::PowerManager::notifier())
105 , status(PowerDevilDaemon::NoAction
) {};
107 Solid::Control::PowerManager::Notifier
* notifier
;
108 QPointer
<Solid::Battery
> battery
;
110 OrgFreedesktopScreenSaverInterface
* screenSaverIface
;
111 OrgKdeKSMServerInterfaceInterface
* ksmServerIface
;
112 OrgKdeScreensaverInterface
* kscreenSaverIface
;
114 KComponentData applicationData
;
115 KSharedConfig::Ptr profilesConfig
;
116 KConfigGroup
* currentConfig
;
117 PollSystemLoader
* pollLoader
;
118 SuspensionLockHandler
* lockHandler
;
120 QString currentProfile
;
121 QStringList availableProfiles
;
123 KNotification
*notification
;
124 QTimer
*notificationTimer
;
126 PowerDevilDaemon::IdleStatus status
;
132 PowerDevilDaemon::PowerDevilDaemon(QObject
*parent
, const QList
<QVariant
>&)
133 : KDEDModule(parent
),
136 KGlobal::locale()->insertCatalog("powerdevil");
138 KAboutData
aboutData("powerdevil", "powerdevil", ki18n("PowerDevil"),
139 POWERDEVIL_VERSION
, ki18n("A Power Management tool for KDE4"),
140 KAboutData::License_GPL
, ki18n("(c) 2008 Dario Freddi"),
141 KLocalizedString(), "http://www.kde.org");
143 aboutData
.addAuthor(ki18n("Dario Freddi"), ki18n("Maintainer"), "drf54321@gmail.com",
144 "http://drfav.wordpress.com");
146 d
->applicationData
= KComponentData(aboutData
);
148 d
->pollLoader
= new PollSystemLoader(this);
149 d
->lockHandler
= new SuspensionLockHandler(this);
150 d
->notificationTimer
= new QTimer(this);
152 /* First things first: PowerDevil might be used when another powermanager is already
153 * on. So we need to check the system bus and see if another known powermanager has
154 * already been registered. Let's see.
157 QDBusConnection conn
= QDBusConnection::systemBus();
159 if (conn
.interface()->isServiceRegistered("org.freedesktop.PowerManagement") ||
160 conn
.interface()->isServiceRegistered("com.novell.powersave") ||
161 conn
.interface()->isServiceRegistered("org.freedesktop.Policy.Power") ||
162 conn
.interface()->isServiceRegistered("org.kde.powerdevilsystem")) {
163 kError() << "PowerDevil not initialized, another power manager has been detected";
167 d
->profilesConfig
= KSharedConfig::openConfig("powerdevilprofilesrc", KConfig::SimpleConfig
);
168 setAvailableProfiles(d
->profilesConfig
->groupList());
170 recacheBatteryPointer(true);
172 // Set up all needed DBus interfaces
173 d
->screenSaverIface
= new OrgFreedesktopScreenSaverInterface("org.freedesktop.ScreenSaver", "/ScreenSaver",
174 QDBusConnection::sessionBus(), this);
175 d
->ksmServerIface
= new OrgKdeKSMServerInterfaceInterface("org.kde.ksmserver", "/KSMServer",
176 QDBusConnection::sessionBus(), this);
177 d
->kscreenSaverIface
= new OrgKdeScreensaverInterface("org.freedesktop.ScreenSaver", "/ScreenSaver",
178 QDBusConnection::sessionBus(), this);
180 connect(d
->notifier
, SIGNAL(buttonPressed(int)), this, SLOT(buttonPressed(int)));
181 connect(d
->notifier
, SIGNAL(batteryRemainingTimeChanged(int)), this, SLOT(batteryRemainingTimeChanged(int)));
182 connect(d
->lockHandler
, SIGNAL(streamCriticalNotification(const QString
&, const QString
&,
183 const char*, const QString
&)),
184 SLOT(emitCriticalNotification(const QString
&, const QString
&,
185 const char*, const QString
&)));
187 /* Time for setting up polling! We can have different methods, so
188 * let's check what we got.
191 if (PowerDevilSettings::pollingSystem() == -1) {
192 // Ok, new configuration... so let's see what we've got!!
194 QMap
<AbstractSystemPoller::PollingType
, QString
> pList
= d
->pollLoader
->getAvailableSystems();
196 if (pList
.contains(AbstractSystemPoller::XSyncBased
)) {
197 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::XSyncBased
);
198 } else if (pList
.contains(AbstractSystemPoller::WidgetBased
)) {
199 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::WidgetBased
);
201 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::TimerBased
);
204 PowerDevilSettings::self()->writeConfig();
207 setUpPollingSystem();
210 new PowerDevilAdaptor(this);
211 new PowerManagementConnector(this);
213 // This gets registered to avoid double copies.
214 QDBusConnection::sessionBus().registerService("org.kde.powerdevilsystem");
216 // All systems up Houston, let's go!
220 PowerDevilDaemon::~PowerDevilDaemon()
225 void PowerDevilDaemon::batteryRemainingTimeChanged(int time
)
227 kDebug() << KGlobal::locale()->formatDuration(time
);
230 SuspensionLockHandler
*PowerDevilDaemon::lockHandler()
232 return d
->lockHandler
;
235 QString
PowerDevilDaemon::profile() const
237 return d
->currentProfile
;
240 bool PowerDevilDaemon::recacheBatteryPointer(bool force
)
242 /* You'll see some switches on d->battery. This is fundamental since PowerDevil might run
243 * also on system without batteries. Most of modern desktop systems support CPU scaling,
244 * so somebody might find PowerDevil handy, and we don't want it to crash on them. To put it
245 * short, we simply bypass all adaptor and battery events if no batteries are found.
249 if (d
->battery
->isValid() && !force
) {
256 // Here we get our battery interface, it will be useful later.
257 foreach(const Solid::Device
&device
, Solid::Device::listFromType(Solid::DeviceInterface::Battery
, QString())) {
258 Solid::Device dev
= device
;
259 Solid::Battery
*b
= qobject_cast
<Solid::Battery
*> (dev
.asDeviceInterface(Solid::DeviceInterface::Battery
));
261 if (b
->type() != Solid::Battery::PrimaryBattery
) {
270 /* Those slots are relevant only if we're on a system that has a battery. If not, we simply don't care
274 connect(d
->notifier
, SIGNAL(acAdapterStateChanged(int)), this, SLOT(acAdapterStateChanged(int)));
276 if (!connect(d
->battery
, SIGNAL(chargePercentChanged(int, const QString
&)), this,
277 SLOT(batteryChargePercentChanged(int, const QString
&)))) {
279 emitCriticalNotification("powerdevilerror", i18n("Could not connect to battery interface!\n"
280 "Please check your system configuration"));
290 void PowerDevilDaemon::setUpPollingSystem()
292 if (!loadPollingSystem((AbstractSystemPoller::PollingType
) PowerDevilSettings::pollingSystem())) {
293 /* Let's try to load each profile one at a time, and then
294 * set the configuration to the profile that worked out.
297 if (loadPollingSystem(AbstractSystemPoller::XSyncBased
)) {
298 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::XSyncBased
);
299 PowerDevilSettings::self()->writeConfig();
303 if (loadPollingSystem(AbstractSystemPoller::WidgetBased
)) {
304 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::WidgetBased
);
305 PowerDevilSettings::self()->writeConfig();
309 if (loadPollingSystem(AbstractSystemPoller::TimerBased
)) {
310 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::TimerBased
);
311 PowerDevilSettings::self()->writeConfig();
315 /* If we're here, we have a big problem, since no polling system has been loaded.
316 * What should we do? For now, let's just spit out a kError
319 kError() << "Could not load a polling system!";
323 bool PowerDevilDaemon::loadPollingSystem(AbstractSystemPoller::PollingType type
)
325 QMap
<AbstractSystemPoller::PollingType
, QString
> pList
= d
->pollLoader
->getAvailableSystems();
327 if (!pList
.contains(type
)) {
330 if (!d
->pollLoader
->loadSystem(type
)) {
335 if (d
->pollLoader
->poller()) {
336 connect(d
->pollLoader
->poller(), SIGNAL(resumingFromIdle()), SLOT(resumeFromIdle()));
337 connect(d
->pollLoader
->poller(), SIGNAL(pollRequest(int)), SLOT(poll(int)));
345 QVariantMap
PowerDevilDaemon::getSupportedPollingSystems()
349 QMap
<int, QString
> pmap
= d
->pollLoader
->getAvailableSystemsAsInt();
351 foreach(int ent
, pmap
.keys()) {
352 map
[pmap
[ent
]] = ent
;
358 void PowerDevilDaemon::resumeFromIdle()
360 KConfigGroup
* settings
= getCurrentProfile();
362 Solid::Control::PowerManager::setBrightness(settings
->readEntry("brightness").toInt());
364 POLLER_CALL(d
->pollLoader
->poller(), stopCatchingIdleEvents());
365 POLLER_CALL(d
->pollLoader
->poller(), forcePollRequest());
368 void PowerDevilDaemon::refreshStatus()
370 /* The configuration could have changed if this function was called, so
373 PowerDevilSettings::self()->readConfig();
374 d
->profilesConfig
->reparseConfiguration();
378 getCurrentProfile(true);
380 /* Let's force status update, if we have a battery. Otherwise, let's just
381 * re-apply the current profile.
384 acAdapterStateChanged(Solid::Control::PowerManager::acAdapterState(), true);
390 void PowerDevilDaemon::acAdapterStateChanged(int state
, bool forced
)
392 if (state
== Solid::Control::PowerManager::Plugged
&& !forced
) {
394 emitNotification("pluggedin", i18n("The power adaptor has been plugged in"));
397 if (state
== Solid::Control::PowerManager::Unplugged
&& !forced
) {
399 emitNotification("unplugged", i18n("The power adaptor has been unplugged"));
403 reloadProfile(state
);
411 int dropError(Display
*, XErrorEvent
*);
412 typedef int (*XErrFunc
)(Display
*, XErrorEvent
*);
415 int dropError(Display
*, XErrorEvent
*)
421 void PowerDevilDaemon::applyProfile()
423 KConfigGroup
* settings
= getCurrentProfile();
428 Solid::Control::PowerManager::setBrightness(settings
->readEntry("brightness").toInt());
429 Solid::Control::PowerManager::setCpuFreqPolicy((Solid::Control::PowerManager::CpuFreqPolicy
)
430 settings
->readEntry("cpuPolicy").toInt());
432 QVariant var
= settings
->readEntry("disabledCPUs", QVariant());
433 QList
<QVariant
> list
= var
.toList();
435 foreach(const Solid::Device
&device
, Solid::Device::listFromType(Solid::DeviceInterface::Processor
, QString())) {
436 Solid::Device d
= device
;
437 Solid::Processor
* processor
= qobject_cast
<Solid::Processor
* > (d
.asDeviceInterface(Solid::DeviceInterface::Processor
));
441 foreach(const QVariant
&ent
, list
) {
442 if (processor
->number() == ent
.toInt()) {
447 Solid::Control::PowerManager::setCpuEnabled(processor
->number(), enable
);
450 Solid::Control::PowerManager::setScheme(settings
->readEntry("scheme"));
452 POLLER_CALL(d
->pollLoader
->poller(), forcePollRequest());
455 void PowerDevilDaemon::setUpDPMS()
457 KConfigGroup
* settings
= getCurrentProfile();
464 defaultHandler
= XSetErrorHandler(dropError
);
466 Display
*dpy
= QX11Info::display();
469 bool has_DPMS
= true;
471 if (!DPMSQueryExtension(dpy
, &dummy
, &dummy
) || !DPMSCapable(dpy
)) {
473 XSetErrorHandler(defaultHandler
);
478 if (settings
->readEntry("DPMSEnabled", false)) {
485 XSetErrorHandler(defaultHandler
);
489 int standby
= 60 * settings
->readEntry("DPMSStandby").toInt();
490 int suspend
= 60 * settings
->readEntry("DPMSSuspend").toInt();
491 int poff
= 60 * settings
->readEntry("DPMSPowerOff").toInt();
493 if (settings
->readEntry("DPMSStandbyEnabled", false)) {
496 if (settings
->readEntry("DPMSSuspendEnabled", false)) {
499 if (settings
->readEntry("DPMSPowerOffEnabled", false)) {
503 DPMSSetTimeouts(dpy
, standby
, suspend
, poff
);
506 XSetErrorHandler(defaultHandler
);
510 // The screen saver depends on the DPMS settings
511 d
->kscreenSaverIface
->configure();
515 void PowerDevilDaemon::batteryChargePercentChanged(int percent
, const QString
&udi
)
522 foreach(const Solid::Device
&device
, Solid::Device::listFromType(Solid::DeviceInterface::Battery
, QString())) {
523 Solid::Device d
= device
;
524 Solid::Battery
*battery
= qobject_cast
<Solid::Battery
*> (d
.asDeviceInterface(Solid::DeviceInterface::Battery
));
525 if (battery
->chargePercent() > 0 && battery
->type() == Solid::Battery::PrimaryBattery
) {
526 charge
+= battery
->chargePercent();
530 setBatteryPercent(charge
);
532 if (Solid::Control::PowerManager::acAdapterState() == Solid::Control::PowerManager::Plugged
)
535 if (charge
<= PowerDevilSettings::batteryCriticalLevel()) {
536 switch (PowerDevilSettings::batLowAction()) {
538 if (PowerDevilSettings::waitBeforeSuspending()) {
539 emitWarningNotification("criticalbattery", i18n("Your battery has reached "
540 "critical level, the PC will be halted in %1 seconds. "
541 "Click here to block the process.",
542 PowerDevilSettings::waitBeforeSuspendingTime()),
549 if (PowerDevilSettings::waitBeforeSuspending()) {
550 emitWarningNotification("criticalbattery", i18n("Your battery has reached "
551 "critical level, the PC will be suspended to disk in "
552 "%1 seconds. Click here to block the process.",
553 PowerDevilSettings::waitBeforeSuspendingTime()),
554 SLOT(suspendToDisk()));
560 if (PowerDevilSettings::waitBeforeSuspending()) {
561 emitWarningNotification("criticalbattery", i18n("Your battery has reached "
562 "critical level, the PC will be suspended to RAM in "
563 "%1 seconds. Click here to block the process",
564 PowerDevilSettings::waitBeforeSuspendingTime()),
565 SLOT(suspendToRam()));
571 if (PowerDevilSettings::waitBeforeSuspending()) {
572 emitWarningNotification("criticalbattery", i18n("Your battery has reached "
573 "critical level, the PC is going Standby in %1 seconds. "
574 "Click here to block the process.",
575 PowerDevilSettings::waitBeforeSuspendingTime()),
582 emitWarningNotification("criticalbattery", i18n("Your battery has reached "
583 "critical level, save your work as soon as possible!"));
586 } else if (charge
== PowerDevilSettings::batteryWarningLevel()) {
587 emitWarningNotification("warningbattery", i18n("Your battery has reached warning level"));
589 } else if (charge
== PowerDevilSettings::batteryLowLevel()) {
590 emitWarningNotification("lowbattery", i18n("Your battery has reached low level"));
595 void PowerDevilDaemon::buttonPressed(int but
)
597 KConfigGroup
* settings
= getCurrentProfile();
602 kDebug() << "A button was pressed, code" << but
;
604 if (but
== Solid::Control::PowerManager::LidClose
) {
606 switch (settings
->readEntry("lidAction").toInt()) {
625 } else if (but
== Solid::Control::PowerManager::PowerButton
) {
627 switch (settings
->readEntry("powerButtonAction").toInt()) {
649 } else if (but
== Solid::Control::PowerManager::SleepButton
) {
651 switch (settings
->readEntry("sleepButtonAction").toInt()) {
676 void PowerDevilDaemon::decreaseBrightness()
678 int currentBrightness
= qMax(0, (int)(Solid::Control::PowerManager::brightness() - 10));
679 Solid::Control::PowerManager::setBrightness(currentBrightness
);
682 void PowerDevilDaemon::increaseBrightness()
684 int currentBrightness
= qMin(100, (int)(Solid::Control::PowerManager::brightness() + 10));
685 Solid::Control::PowerManager::setBrightness(currentBrightness
);
688 void PowerDevilDaemon::shutdownNotification(bool automated
)
690 if (!d
->lockHandler
->setNotificationLock(automated
)) {
694 if (PowerDevilSettings::waitBeforeSuspending()) {
695 emitNotification("doingjob", i18n("The computer will be halted in %1 seconds. Click "
696 "here to block the process.",
697 PowerDevilSettings::waitBeforeSuspendingTime()),
704 void PowerDevilDaemon::suspendToDiskNotification(bool automated
)
706 if (!d
->lockHandler
->setNotificationLock(automated
)) {
710 if (PowerDevilSettings::waitBeforeSuspending()) {
711 emitNotification("doingjob", i18n("The computer will be suspended to disk in %1 "
712 "seconds. Click here to block the process.",
713 PowerDevilSettings::waitBeforeSuspendingTime()),
714 SLOT(suspendToDisk()));
720 void PowerDevilDaemon::suspendToRamNotification(bool automated
)
722 if (!d
->lockHandler
->setNotificationLock(automated
)) {
726 if (PowerDevilSettings::waitBeforeSuspending()) {
727 emitNotification("doingjob", i18n("The computer will be suspended to RAM in %1 "
728 "seconds. Click here to block the process.",
729 PowerDevilSettings::waitBeforeSuspendingTime()),
730 SLOT(suspendToRam()));
736 void PowerDevilDaemon::standbyNotification(bool automated
)
738 if (!d
->lockHandler
->setNotificationLock(automated
)) {
742 if (PowerDevilSettings::waitBeforeSuspending()) {
743 emitNotification("doingjob", i18n("The computer will be put into standby in %1 "
744 "seconds. Click here to block the process.",
745 PowerDevilSettings::waitBeforeSuspendingTime()),
752 void PowerDevilDaemon::shutdown(bool automated
)
754 if (!d
->lockHandler
->setJobLock(automated
)) {
758 d
->ksmServerIface
->logout((int)KWorkSpace::ShutdownConfirmNo
, (int)KWorkSpace::ShutdownTypeHalt
,
759 (int)KWorkSpace::ShutdownModeTryNow
);
761 d
->lockHandler
->releaseAllLocks();
764 void PowerDevilDaemon::shutdownDialog()
766 d
->ksmServerIface
->logout((int)KWorkSpace::ShutdownConfirmYes
, (int)KWorkSpace::ShutdownTypeNone
,
767 (int)KWorkSpace::ShutdownModeDefault
);
770 void PowerDevilDaemon::suspendToDisk(bool automated
)
772 if (!d
->lockHandler
->setJobLock(automated
)) {
776 POLLER_CALL(d
->pollLoader
->poller(), simulateUserActivity()); //prevent infinite suspension loops
778 if (PowerDevilSettings::configLockScreen()) {
782 KJob
* job
= Solid::Control::PowerManager::suspend(Solid::Control::PowerManager::ToDisk
);
783 connect(job
, SIGNAL(result(KJob
*)), this, SLOT(suspendJobResult(KJob
*)));
787 QTimer::singleShot(10000, d
->lockHandler
, SLOT(releaseAllLocks()));
790 void PowerDevilDaemon::suspendToRam(bool automated
)
792 if (!d
->lockHandler
->setJobLock(automated
)) {
796 POLLER_CALL(d
->pollLoader
->poller(), simulateUserActivity()); //prevent infinite suspension loops
798 if (PowerDevilSettings::configLockScreen()) {
802 KJob
* job
= Solid::Control::PowerManager::suspend(Solid::Control::PowerManager::ToRam
);
803 connect(job
, SIGNAL(result(KJob
*)), this, SLOT(suspendJobResult(KJob
*)));
806 QTimer::singleShot(10000, d
->lockHandler
, SLOT(releaseAllLocks()));
809 void PowerDevilDaemon::standby(bool automated
)
811 if (!d
->lockHandler
->setJobLock(automated
)) {
815 POLLER_CALL(d
->pollLoader
->poller(), simulateUserActivity()); //prevent infinite suspension loops
817 if (PowerDevilSettings::configLockScreen()) {
821 KJob
* job
= Solid::Control::PowerManager::suspend(Solid::Control::PowerManager::Standby
);
822 connect(job
, SIGNAL(result(KJob
*)), this, SLOT(suspendJobResult(KJob
*)));
826 QTimer::singleShot(10000, d
->lockHandler
, SLOT(releaseAllLocks()));
829 void PowerDevilDaemon::suspendJobResult(KJob
* job
)
832 emitCriticalNotification("joberror", QString(i18n("There was an error while suspending:")
833 + QChar('\n') + job
->errorString()));
836 POLLER_CALL(d
->pollLoader
->poller(), simulateUserActivity()); //prevent infinite suspension loops
838 kDebug() << "Resuming from suspension";
840 d
->lockHandler
->releaseAllLocks();
845 void PowerDevilDaemon::poll(int idle
)
847 /* The polling system is abstract. This function gets called when the current
848 * system requests it. Usually, it is called only when needed (if you're using
849 * an efficient backend such as XSync).
850 * We make an intensive use of qMin/qMax here to determine the minimum time.
853 kDebug() << "Polling started, idle time is" << idle
<< "seconds";
855 KConfigGroup
* settings
= getCurrentProfile();
861 if (!settings
->readEntry("dimOnIdle", false) && !settings
->readEntry("turnOffIdle", false) &&
862 settings
->readEntry("idleAction").toInt() == None
) {
863 kDebug() << "Stopping timer";
864 POLLER_CALL(d
->pollLoader
->poller(), stopCatchingTimeouts());
868 int dimOnIdleTime
= settings
->readEntry("dimOnIdleTime").toInt() * 60;
869 int minDimTime
= dimOnIdleTime
* 1 / 2;
870 int minDimEvent
= dimOnIdleTime
;
872 if (idle
< (dimOnIdleTime
* 3 / 4)) {
873 minDimEvent
= dimOnIdleTime
* 3 / 4;
875 if (idle
< (dimOnIdleTime
* 1 / 2)) {
876 minDimEvent
= dimOnIdleTime
* 1 / 2;
879 int minTime
= settings
->readEntry("idleTime").toInt() * 60;
881 if (settings
->readEntry("turnOffIdle", false)) {
882 minTime
= qMin(minTime
, settings
->readEntry("turnOffIdleTime").toInt() * 60);
884 if (settings
->readEntry("dimOnIdle", false)) {
885 minTime
= qMin(minTime
, minDimTime
);
888 kDebug() << "Minimum time is" << minTime
<< "seconds";
890 if (idle
< minTime
) {
891 d
->status
= NoAction
;
892 int remaining
= minTime
- idle
;
893 POLLER_CALL(d
->pollLoader
->poller(), setNextTimeout(remaining
* 1000));
894 kDebug() << "Nothing to do, next event in" << remaining
<< "seconds";
898 /* You'll see we release input lock here sometimes. Why? Well,
899 * after some tests, I found out that the offscreen widget doesn't work
900 * if the monitor gets turned off or the PC is suspended. But, we don't care
901 * about this for a simple reason: the only parameter we need to look into
902 * is the brightness, so we just release the lock, set back the brightness
903 * to normal, and that's it.
906 if (idle
>= settings
->readEntry("idleTime").toInt() * 60) {
907 setUpNextTimeout(idle
, minDimEvent
);
909 if (d
->status
== Action
) {
915 switch (settings
->readEntry("idleAction").toInt()) {
917 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
918 shutdownNotification(true);
921 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
922 suspendToDiskNotification(true);
925 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
926 suspendToRamNotification(true);
929 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
930 standbyNotification(true);
933 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
942 } else if (settings
->readEntry("dimOnIdle", false)
943 && (idle
>= dimOnIdleTime
)) {
944 if (d
->status
!= DimTotal
) {
945 d
->status
= DimTotal
;
946 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
947 Solid::Control::PowerManager::setBrightness(0);
949 } else if (settings
->readEntry("dimOnIdle", false)
950 && (idle
>= (dimOnIdleTime
* 3 / 4))) {
951 if (d
->status
!= DimThreeQuarters
) {
952 d
->status
= DimThreeQuarters
;
953 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
954 float newBrightness
= Solid::Control::PowerManager::brightness() / 4;
955 Solid::Control::PowerManager::setBrightness(newBrightness
);
957 } else if (settings
->readEntry("dimOnIdle", false) &&
958 (idle
>= (dimOnIdleTime
* 1 / 2))) {
959 if (d
->status
!= DimHalf
) {
961 POLLER_CALL(d
->pollLoader
->poller(), catchIdleEvent());
962 float newBrightness
= Solid::Control::PowerManager::brightness() / 2;
963 Solid::Control::PowerManager::setBrightness(newBrightness
);
966 d
->status
= NoAction
;
967 POLLER_CALL(d
->pollLoader
->poller(), stopCatchingIdleEvents());
968 Solid::Control::PowerManager::setBrightness(settings
->readEntry("brightness").toInt());
971 setUpNextTimeout(idle
, minDimEvent
);
974 void PowerDevilDaemon::setUpNextTimeout(int idle
, int minDimEvent
)
976 KConfigGroup
*settings
= getCurrentProfile();
978 int nextTimeout
= -1;
980 if ((settings
->readEntry("idleTime").toInt() * 60) > idle
) {
981 if (nextTimeout
>= 0) {
982 nextTimeout
= qMin(nextTimeout
, (settings
->readEntry("idleTime").toInt() * 60) - idle
);
984 nextTimeout
= (settings
->readEntry("idleTime").toInt() * 60) - idle
;
987 if (minDimEvent
> idle
&& settings
->readEntry("dimOnIdle", false)) {
988 if (nextTimeout
>= 0) {
989 nextTimeout
= qMin(nextTimeout
, minDimEvent
- idle
);
991 nextTimeout
= minDimEvent
- idle
;
995 if (nextTimeout
>= 0) {
996 POLLER_CALL(d
->pollLoader
->poller(), setNextTimeout(nextTimeout
* 1000));
997 kDebug() << "Next timeout in" << nextTimeout
<< "seconds";
999 POLLER_CALL(d
->pollLoader
->poller(), stopCatchingTimeouts());
1000 kDebug() << "Stopping timer";
1004 void PowerDevilDaemon::lockScreen()
1006 emitNotification("doingjob", i18n("The screen is being locked"));
1007 d
->screenSaverIface
->Lock();
1010 void PowerDevilDaemon::emitCriticalNotification(const QString
&evid
, const QString
&message
,
1011 const char *slot
, const QString
&iconname
)
1013 /* Those notifications are always displayed */
1015 KNotification::event(evid
, message
, KIcon(iconname
).pixmap(20, 20),
1016 0, KNotification::CloseOnTimeout
, d
->applicationData
);
1018 d
->notification
= KNotification::event(evid
, message
, KIcon(iconname
).pixmap(20, 20),
1019 0, KNotification::Persistent
, d
->applicationData
);
1021 connect(d
->notificationTimer
, SIGNAL(timeout()), slot
);
1022 connect(d
->notificationTimer
, SIGNAL(timeout()), SLOT(cleanUpTimer()));
1024 d
->lockHandler
->connect(d
->notification
, SIGNAL(closed()), d
->lockHandler
, SLOT(releaseNotificationLock()));
1025 connect(d
->notification
, SIGNAL(closed()), SLOT(cleanUpTimer()));
1027 d
->notificationTimer
->start(PowerDevilSettings::waitBeforeSuspendingTime() * 1000);
1031 void PowerDevilDaemon::emitWarningNotification(const QString
&evid
, const QString
&message
,
1032 const char *slot
, const QString
&iconname
)
1034 if (!PowerDevilSettings::enableWarningNotifications()) {
1036 QTimer::singleShot(0, this, slot
);
1042 KNotification::event(evid
, message
, KIcon(iconname
).pixmap(20, 20),
1043 0, KNotification::CloseOnTimeout
, d
->applicationData
);
1045 d
->notification
= KNotification::event(evid
, message
, KIcon(iconname
).pixmap(20, 20),
1046 0, KNotification::Persistent
, d
->applicationData
);
1048 connect(d
->notificationTimer
, SIGNAL(timeout()), slot
);
1049 connect(d
->notificationTimer
, SIGNAL(timeout()), SLOT(cleanUpTimer()));
1051 d
->lockHandler
->connect(d
->notification
, SIGNAL(closed()), d
->lockHandler
, SLOT(releaseNotificationLock()));
1052 connect(d
->notification
, SIGNAL(closed()), SLOT(cleanUpTimer()));
1054 d
->notificationTimer
->start(PowerDevilSettings::waitBeforeSuspendingTime() * 1000);
1058 void PowerDevilDaemon::emitNotification(const QString
&evid
, const QString
&message
,
1059 const char *slot
, const QString
&iconname
)
1061 if (!PowerDevilSettings::enableNotifications()) {
1063 QTimer::singleShot(0, this, slot
);
1069 KNotification::event(evid
, message
, KIcon(iconname
).pixmap(20, 20),
1070 0, KNotification::CloseOnTimeout
, d
->applicationData
);
1072 d
->notification
= KNotification::event(evid
, message
, KIcon(iconname
).pixmap(20, 20),
1073 0, KNotification::Persistent
, d
->applicationData
);
1075 connect(d
->notificationTimer
, SIGNAL(timeout()), slot
);
1076 connect(d
->notificationTimer
, SIGNAL(timeout()), SLOT(cleanUpTimer()));
1078 d
->lockHandler
->connect(d
->notification
, SIGNAL(closed()), d
->lockHandler
, SLOT(releaseNotificationLock()));
1079 connect(d
->notification
, SIGNAL(closed()), SLOT(cleanUpTimer()));
1081 d
->notificationTimer
->start(PowerDevilSettings::waitBeforeSuspendingTime() * 1000);
1085 void PowerDevilDaemon::cleanUpTimer()
1087 kDebug() << "Disconnecting signals";
1089 d
->notificationTimer
->disconnect();
1090 d
->notification
->disconnect();
1091 d
->notificationTimer
->stop();
1093 if (d
->notification
) {
1094 d
->notification
->deleteLater();
1098 KConfigGroup
* PowerDevilDaemon::getCurrentProfile(bool forcereload
)
1100 /* We need to access this a lot of times, so we use a cached
1101 * implementation here. We create the object just if we're sure
1102 * it is not already valid.
1104 * IMPORTANT!!! This class already handles deletion of the config
1105 * object, so you don't have to delete it!!
1108 if (d
->currentConfig
) { // This HAS to be kept, since d->currentConfig could be not valid!!
1109 if (forcereload
|| d
->currentConfig
->name() != d
->currentProfile
) {
1110 delete d
->currentConfig
;
1111 d
->currentConfig
= 0;
1115 if (!d
->currentConfig
) {
1116 d
->currentConfig
= new KConfigGroup(d
->profilesConfig
, d
->currentProfile
);
1119 if (!d
->currentConfig
->isValid() || !d
->currentConfig
->entryMap().size()) {
1120 emitCriticalNotification("powerdevilerror", i18n("The profile \"%1\" has been selected, "
1121 "but it does not exist!\nPlease check your PowerDevil configuration.",
1122 d
->currentProfile
));
1124 delete d
->currentConfig
;
1125 d
->currentConfig
= 0;
1128 return d
->currentConfig
;
1131 void PowerDevilDaemon::reloadProfile(int state
)
1133 if (!recacheBatteryPointer()) {
1134 setCurrentProfile(PowerDevilSettings::aCProfile());
1137 state
= Solid::Control::PowerManager::acAdapterState();
1140 if (state
== Solid::Control::PowerManager::Plugged
) {
1141 setCurrentProfile(PowerDevilSettings::aCProfile());
1142 } else if (d
->battery
->chargePercent() <= PowerDevilSettings::batteryWarningLevel()) {
1143 setCurrentProfile(PowerDevilSettings::warningProfile());
1144 } else if (d
->battery
->chargePercent() <= PowerDevilSettings::batteryLowLevel()) {
1145 setCurrentProfile(PowerDevilSettings::lowProfile());
1147 setCurrentProfile(PowerDevilSettings::batteryProfile());
1151 if (d
->currentProfile
.isEmpty()) {
1152 /* Ok, misconfiguration! Well, first things first: if we have some profiles,
1153 * let's just load the first available one.
1156 if (!d
->availableProfiles
.isEmpty()) {
1157 setCurrentProfile(d
->availableProfiles
.at(0));
1159 /* In this case, let's fill our profiles file with our
1160 * wonderful defaults!
1163 restoreDefaultProfiles();
1165 PowerDevilSettings::setACProfile("Performance");
1166 PowerDevilSettings::setBatteryProfile("Powersave");
1167 PowerDevilSettings::setLowProfile("Aggressive Powersave");
1168 PowerDevilSettings::setWarningProfile("Xtreme Powersave");
1170 PowerDevilSettings::self()->writeConfig();
1178 POLLER_CALL(d
->pollLoader
->poller(), forcePollRequest());
1181 void PowerDevilDaemon::setProfile(const QString
& profile
)
1183 setCurrentProfile(profile
);
1185 /* Don't call refreshStatus() here, since we don't want the predefined profile
1191 KConfigGroup
* settings
= getCurrentProfile();
1193 emitNotification("profileset", i18n("Profile changed to \"%1\"", profile
),
1194 0, settings
->readEntry("iconname"));
1197 void PowerDevilDaemon::reloadAndStream()
1201 setAvailableProfiles(d
->profilesConfig
->groupList());
1208 void PowerDevilDaemon::streamData()
1210 emit
profileChanged(d
->currentProfile
, d
->availableProfiles
);
1211 emit
stateChanged(d
->batteryPercent
, d
->isPlugged
);
1214 QVariantMap
PowerDevilDaemon::getSupportedGovernors()
1216 QVariantMap retlist
;
1218 Solid::Control::PowerManager::CpuFreqPolicies policies
= Solid::Control::PowerManager::supportedCpuFreqPolicies();
1220 if (policies
& Solid::Control::PowerManager::Performance
) {
1221 retlist
[i18n("Performance")] = (int) Solid::Control::PowerManager::Performance
;
1224 if (policies
& Solid::Control::PowerManager::OnDemand
) {
1225 retlist
[i18n("Dynamic (ondemand)")] = (int) Solid::Control::PowerManager::OnDemand
;
1228 if (policies
& Solid::Control::PowerManager::Conservative
) {
1229 retlist
[i18n("Dynamic (conservative)")] = (int) Solid::Control::PowerManager::Conservative
;
1232 if (policies
& Solid::Control::PowerManager::Powersave
) {
1233 retlist
[i18n("Powersave")] = (int) Solid::Control::PowerManager::Powersave
;
1236 if (policies
& Solid::Control::PowerManager::Userspace
) {
1237 retlist
[i18n("Userspace")] = (int) Solid::Control::PowerManager::Userspace
;
1243 QVariantMap
PowerDevilDaemon::getSupportedSuspendMethods()
1245 QVariantMap retlist
;
1247 Solid::Control::PowerManager::SuspendMethods methods
= Solid::Control::PowerManager::supportedSuspendMethods();
1249 if (methods
& Solid::Control::PowerManager::ToDisk
) {
1250 retlist
[i18n("Suspend to Disk")] = (int) S2Disk
;
1253 if (methods
& Solid::Control::PowerManager::ToRam
) {
1254 retlist
[i18n("Suspend to Ram")] = (int) S2Ram
;
1257 if (methods
& Solid::Control::PowerManager::Standby
) {
1258 retlist
[i18n("Standby")] = (int) Standby
;
1264 QStringList
PowerDevilDaemon::getSupportedSchemes()
1266 return Solid::Control::PowerManager::supportedSchemes();
1269 void PowerDevilDaemon::setPowersavingScheme(const QString
&scheme
)
1271 Solid::Control::PowerManager::setScheme(scheme
);
1274 void PowerDevilDaemon::setGovernor(int governor
)
1276 Solid::Control::PowerManager::setCpuFreqPolicy((Solid::Control::PowerManager::CpuFreqPolicy
) governor
);
1279 void PowerDevilDaemon::suspend(int method
)
1281 switch ((IdleAction
) method
) {
1283 QTimer::singleShot(100, this, SLOT(suspendToDisk()));
1286 QTimer::singleShot(100, this, SLOT(suspendToRam()));
1289 QTimer::singleShot(100, this, SLOT(standby()));
1296 void PowerDevilDaemon::setBrightness(int value
)
1299 // Then set brightness to half the current brightness.
1301 float b
= Solid::Control::PowerManager::brightness() / 2;
1302 Solid::Control::PowerManager::setBrightness(b
);
1306 Solid::Control::PowerManager::setBrightness(value
);
1309 void PowerDevilDaemon::turnOffScreen()
1315 Display
*dpy
= QX11Info::display();
1317 DPMSInfo(dpy
, &dummy
, &enabled
);
1320 DPMSForceLevel(dpy
, DPMSModeOff
);
1323 DPMSForceLevel(dpy
, DPMSModeOff
);
1328 void PowerDevilDaemon::profileFirstLoad()
1330 KConfigGroup
* settings
= getCurrentProfile();
1335 kDebug() << "Profile initialization";
1337 if (!settings
->readEntry("scriptpath", QString()).isEmpty()) {
1338 QProcess::startDetached(settings
->readEntry("scriptpath"));
1343 if (settings
->readEntry("disableCompositing", false)) {
1344 if (toggleCompositing(false)) {
1345 PowerDevilSettings::setCompositingChanged(true);
1346 PowerDevilSettings::self()->writeConfig();
1348 } else if (PowerDevilSettings::compositingChanged()) {
1349 toggleCompositing(true);
1350 PowerDevilSettings::setCompositingChanged(false);
1351 PowerDevilSettings::self()->writeConfig();
1354 if (PowerDevilSettings::manageDPMS()) {
1355 QTimer::singleShot(300, this, SLOT(setUpDPMS()));
1359 bool PowerDevilDaemon::toggleCompositing(bool enabled
)
1361 KSharedConfigPtr KWinConfig
= KSharedConfig::openConfig("kwinrc");
1362 KConfigGroup
config(KWinConfig
, "Compositing");
1363 bool state
= config
.readEntry("Enabled", false);
1365 if (state
!= enabled
) {
1366 config
.writeEntry("Enabled", enabled
);
1368 QDBusMessage message
= QDBusMessage::createSignal("/KWin",
1371 QDBusConnection::sessionBus().send(message
);
1379 void PowerDevilDaemon::restoreDefaultProfiles()
1381 QString path
= QString("%1/default.powerdevilprofiles").arg(DATA_DIRECTORY
);
1383 KConfig
toImport(path
, KConfig::SimpleConfig
);
1385 foreach(const QString
&ent
, toImport
.groupList()) {
1386 KConfigGroup
copyFrom(&toImport
, ent
);
1387 KConfigGroup
copyTo(d
->profilesConfig
, ent
);
1389 copyFrom
.copyTo(©To
);
1392 d
->profilesConfig
->sync();
1395 void PowerDevilDaemon::setBatteryPercent(int newpercent
)
1397 d
->batteryPercent
= newpercent
;
1398 emit
stateChanged(d
->batteryPercent
, d
->isPlugged
);
1401 void PowerDevilDaemon::setACPlugged(bool newplugged
)
1403 d
->isPlugged
= newplugged
;
1404 emit
stateChanged(d
->batteryPercent
, d
->isPlugged
);
1407 void PowerDevilDaemon::setCurrentProfile(const QString
&profile
)
1409 if (profile
!= d
->currentProfile
) {
1410 d
->currentProfile
= profile
;
1412 emit
profileChanged(d
->currentProfile
, d
->availableProfiles
);
1416 void PowerDevilDaemon::setAvailableProfiles(const QStringList
&aProfiles
)
1418 d
->availableProfiles
= aProfiles
;
1419 emit
profileChanged(d
->currentProfile
, d
->availableProfiles
);
1422 #include "PowerDevilDaemon.moc"