dtor first
[personal-kdebase.git] / workspace / powerdevil / daemon / PowerDevilDaemon.cpp
blob031925e068aa06d56ef64de082083ecd0936da84
1 /***************************************************************************
2 * Copyright (C) 2008 by Dario Freddi <drf@kdemod.ath.cx> *
3 * *
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. *
8 * *
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. *
13 * *
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 ***************************************************************************
19 * *
20 * Part of the code in this file was taken from KDE4Powersave and/or *
21 * Lithium, where noticed. *
22 * *
23 **************************************************************************/
25 #include "PowerDevilDaemon.h"
27 #include <kdemacros.h>
28 #include <KAboutData>
29 #include <KPluginFactory>
30 #include <KNotification>
31 #include <KIcon>
32 #include <KMessageBox>
33 #include <kpluginfactory.h>
34 #include <klocalizedstring.h>
35 #include <kjob.h>
36 #include <kworkspace/kworkspace.h>
37 #include <KApplication>
39 #include <QWidget>
40 #include <QTimer>
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>
59 #ifdef HAVE_DPMS
60 #include <X11/Xmd.h>
61 #include <X11/X.h>
62 #include <X11/Xlib.h>
63 #include <X11/Xatom.h>
64 #include <X11/Xutil.h>
65 #include <X11/Xos.h>
66 extern "C"
68 #include <X11/extensions/dpms.h>
69 int __kde_do_not_unload = 1;
71 #ifndef HAVE_DPMSCAPABLE_PROTO
72 Bool DPMSCapable(Display *);
73 #endif
75 #ifndef HAVE_DPMSINFO_PROTO
76 Status DPMSInfo(Display *, CARD16 *, BOOL *);
77 #endif
80 static XErrorHandler defaultHandler;
82 #endif
84 #define POLLER_CALL(Object, Method) \
85 if (Object != 0) { \
86 AbstractSystemPoller *t = qobject_cast<AbstractSystemPoller *>(Object); \
87 if (t!=0) { \
88 t->Method; \
89 } \
90 } else { \
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
100 public:
101 explicit Private()
102 : notifier(Solid::Control::PowerManager::notifier())
103 , battery(0)
104 , currentConfig(0)
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;
128 int batteryPercent;
129 bool isPlugged;
132 PowerDevilDaemon::PowerDevilDaemon(QObject *parent, const QList<QVariant>&)
133 : KDEDModule(parent),
134 d(new Private())
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";
164 return;
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);
200 } else {
201 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::TimerBased);
204 PowerDevilSettings::self()->writeConfig();
207 setUpPollingSystem();
209 //DBus
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!
217 refreshStatus();
220 PowerDevilDaemon::~PowerDevilDaemon()
222 delete d;
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.
248 if (d->battery) {
249 if (d->battery->isValid() && !force) {
250 return true;
254 d->battery = 0;
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) {
262 continue;
265 if (b->isValid()) {
266 d->battery = b;
270 /* Those slots are relevant only if we're on a system that has a battery. If not, we simply don't care
271 * about them.
273 if (d->battery) {
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"));
281 return false;
283 } else {
284 return false;
287 return true;
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();
300 return;
303 if (loadPollingSystem(AbstractSystemPoller::WidgetBased)) {
304 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::WidgetBased);
305 PowerDevilSettings::self()->writeConfig();
306 return;
309 if (loadPollingSystem(AbstractSystemPoller::TimerBased)) {
310 PowerDevilSettings::setPollingSystem(AbstractSystemPoller::TimerBased);
311 PowerDevilSettings::self()->writeConfig();
312 return;
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)) {
328 return false;
329 } else {
330 if (!d->pollLoader->loadSystem(type)) {
331 return false;
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)));
338 } else {
339 return false;
342 return true;
345 QVariantMap PowerDevilDaemon::getSupportedPollingSystems()
347 QVariantMap map;
349 QMap<int, QString> pmap = d->pollLoader->getAvailableSystemsAsInt();
351 foreach(int ent, pmap.keys()) {
352 map[pmap[ent]] = ent;
355 return map;
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
371 * let's resync it.
373 PowerDevilSettings::self()->readConfig();
374 d->profilesConfig->reparseConfiguration();
376 reloadProfile();
378 getCurrentProfile(true);
380 /* Let's force status update, if we have a battery. Otherwise, let's just
381 * re-apply the current profile.
383 if (d->battery) {
384 acAdapterStateChanged(Solid::Control::PowerManager::acAdapterState(), true);
385 } else {
386 applyProfile();
390 void PowerDevilDaemon::acAdapterStateChanged(int state, bool forced)
392 if (state == Solid::Control::PowerManager::Plugged && !forced) {
393 setACPlugged(true);
394 emitNotification("pluggedin", i18n("The power adaptor has been plugged in"));
397 if (state == Solid::Control::PowerManager::Unplugged && !forced) {
398 setACPlugged(false);
399 emitNotification("unplugged", i18n("The power adaptor has been unplugged"));
402 if (!forced)
403 reloadProfile(state);
405 applyProfile();
408 #ifdef HAVE_DPMS
409 extern "C"
411 int dropError(Display *, XErrorEvent *);
412 typedef int (*XErrFunc)(Display *, XErrorEvent *);
415 int dropError(Display *, XErrorEvent *)
417 return 0;
419 #endif
421 void PowerDevilDaemon::applyProfile()
423 KConfigGroup * settings = getCurrentProfile();
425 if (!settings)
426 return;
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));
439 bool enable = true;
441 foreach(const QVariant &ent, list) {
442 if (processor->number() == ent.toInt()) {
443 enable = false;
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();
459 if (!settings)
460 return;
462 #ifdef HAVE_DPMS
464 defaultHandler = XSetErrorHandler(dropError);
466 Display *dpy = QX11Info::display();
468 int dummy;
469 bool has_DPMS = true;
471 if (!DPMSQueryExtension(dpy, &dummy, &dummy) || !DPMSCapable(dpy)) {
472 has_DPMS = false;
473 XSetErrorHandler(defaultHandler);
476 if (has_DPMS) {
478 if (settings->readEntry("DPMSEnabled", false)) {
479 DPMSEnable(dpy);
480 } else {
481 DPMSDisable(dpy);
484 XFlush(dpy);
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)) {
494 standby = 0;
496 if (settings->readEntry("DPMSSuspendEnabled", false)) {
497 suspend = 0;
499 if (settings->readEntry("DPMSPowerOffEnabled", false)) {
500 poff = 0;
503 DPMSSetTimeouts(dpy, standby, suspend, poff);
505 XFlush(dpy);
506 XSetErrorHandler(defaultHandler);
510 // The screen saver depends on the DPMS settings
511 d->kscreenSaverIface->configure();
512 #endif
515 void PowerDevilDaemon::batteryChargePercentChanged(int percent, const QString &udi)
517 Q_UNUSED(udi)
518 Q_UNUSED(percent)
520 int charge = 0;
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)
533 return;
535 if (charge <= PowerDevilSettings::batteryCriticalLevel()) {
536 switch (PowerDevilSettings::batLowAction()) {
537 case Shutdown:
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()),
543 SLOT(shutdown()));
544 } else {
545 shutdown();
547 break;
548 case S2Disk:
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()));
555 } else {
556 suspendToDisk();
558 break;
559 case S2Ram:
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()));
566 } else {
567 suspendToRam();
569 break;
570 case Standby:
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()),
576 SLOT(standby()));
577 } else {
578 standby();
580 break;
581 default:
582 emitWarningNotification("criticalbattery", i18n("Your battery has reached "
583 "critical level, save your work as soon as possible!"));
584 break;
586 } else if (charge == PowerDevilSettings::batteryWarningLevel()) {
587 emitWarningNotification("warningbattery", i18n("Your battery has reached warning level"));
588 refreshStatus();
589 } else if (charge == PowerDevilSettings::batteryLowLevel()) {
590 emitWarningNotification("lowbattery", i18n("Your battery has reached low level"));
591 refreshStatus();
595 void PowerDevilDaemon::buttonPressed(int but)
597 KConfigGroup * settings = getCurrentProfile();
599 if (!settings)
600 return;
602 kDebug() << "A button was pressed, code" << but;
604 if (but == Solid::Control::PowerManager::LidClose) {
606 switch (settings->readEntry("lidAction").toInt()) {
607 case Shutdown:
608 shutdown();
609 break;
610 case S2Disk:
611 suspendToDisk();
612 break;
613 case S2Ram:
614 suspendToRam();
615 break;
616 case Standby:
617 standby();
618 break;
619 case Lock:
620 lockScreen();
621 break;
622 default:
623 break;
625 } else if (but == Solid::Control::PowerManager::PowerButton) {
627 switch (settings->readEntry("powerButtonAction").toInt()) {
628 case Shutdown:
629 shutdown();
630 break;
631 case S2Disk:
632 suspendToDisk();
633 break;
634 case S2Ram:
635 suspendToRam();
636 break;
637 case Standby:
638 standby();
639 break;
640 case Lock:
641 lockScreen();
642 break;
643 case ShutdownDialog:
644 shutdownDialog();
645 break;
646 default:
647 break;
649 } else if (but == Solid::Control::PowerManager::SleepButton) {
651 switch (settings->readEntry("sleepButtonAction").toInt()) {
652 case Shutdown:
653 shutdown();
654 break;
655 case S2Disk:
656 suspendToDisk();
657 break;
658 case S2Ram:
659 suspendToRam();
660 break;
661 case Standby:
662 standby();
663 break;
664 case Lock:
665 lockScreen();
666 break;
667 case ShutdownDialog:
668 shutdownDialog();
669 break;
670 default:
671 break;
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)) {
691 return;
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()),
698 SLOT(shutdown()));
699 } else {
700 shutdown();
704 void PowerDevilDaemon::suspendToDiskNotification(bool automated)
706 if (!d->lockHandler->setNotificationLock(automated)) {
707 return;
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()));
715 } else {
716 suspendToDisk();
720 void PowerDevilDaemon::suspendToRamNotification(bool automated)
722 if (!d->lockHandler->setNotificationLock(automated)) {
723 return;
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()));
731 } else {
732 suspendToRam();
736 void PowerDevilDaemon::standbyNotification(bool automated)
738 if (!d->lockHandler->setNotificationLock(automated)) {
739 return;
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()),
746 SLOT(standby()));
747 } else {
748 standby();
752 void PowerDevilDaemon::shutdown(bool automated)
754 if (!d->lockHandler->setJobLock(automated)) {
755 return;
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)) {
773 return;
776 POLLER_CALL(d->pollLoader->poller(), simulateUserActivity()); //prevent infinite suspension loops
778 if (PowerDevilSettings::configLockScreen()) {
779 lockScreen();
782 KJob * job = Solid::Control::PowerManager::suspend(Solid::Control::PowerManager::ToDisk);
783 connect(job, SIGNAL(result(KJob *)), this, SLOT(suspendJobResult(KJob *)));
784 job->start();
786 // Temporary hack...
787 QTimer::singleShot(10000, d->lockHandler, SLOT(releaseAllLocks()));
790 void PowerDevilDaemon::suspendToRam(bool automated)
792 if (!d->lockHandler->setJobLock(automated)) {
793 return;
796 POLLER_CALL(d->pollLoader->poller(), simulateUserActivity()); //prevent infinite suspension loops
798 if (PowerDevilSettings::configLockScreen()) {
799 lockScreen();
802 KJob * job = Solid::Control::PowerManager::suspend(Solid::Control::PowerManager::ToRam);
803 connect(job, SIGNAL(result(KJob *)), this, SLOT(suspendJobResult(KJob *)));
804 job->start();
805 // Temporary hack...
806 QTimer::singleShot(10000, d->lockHandler, SLOT(releaseAllLocks()));
809 void PowerDevilDaemon::standby(bool automated)
811 if (!d->lockHandler->setJobLock(automated)) {
812 return;
815 POLLER_CALL(d->pollLoader->poller(), simulateUserActivity()); //prevent infinite suspension loops
817 if (PowerDevilSettings::configLockScreen()) {
818 lockScreen();
821 KJob * job = Solid::Control::PowerManager::suspend(Solid::Control::PowerManager::Standby);
822 connect(job, SIGNAL(result(KJob *)), this, SLOT(suspendJobResult(KJob *)));
823 job->start();
825 // Temporary hack...
826 QTimer::singleShot(10000, d->lockHandler, SLOT(releaseAllLocks()));
829 void PowerDevilDaemon::suspendJobResult(KJob * job)
831 if (job->error()) {
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();
842 job->deleteLater();
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();
857 if (!settings) {
858 return;
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());
865 return;
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";
895 return;
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) {
910 return;
913 d->status = Action;
915 switch (settings->readEntry("idleAction").toInt()) {
916 case Shutdown:
917 POLLER_CALL(d->pollLoader->poller(), catchIdleEvent());
918 shutdownNotification(true);
919 break;
920 case S2Disk:
921 POLLER_CALL(d->pollLoader->poller(), catchIdleEvent());
922 suspendToDiskNotification(true);
923 break;
924 case S2Ram:
925 POLLER_CALL(d->pollLoader->poller(), catchIdleEvent());
926 suspendToRamNotification(true);
927 break;
928 case Standby:
929 POLLER_CALL(d->pollLoader->poller(), catchIdleEvent());
930 standbyNotification(true);
931 break;
932 case Lock:
933 POLLER_CALL(d->pollLoader->poller(), catchIdleEvent());
934 lockScreen();
935 break;
936 default:
937 break;
940 return;
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) {
960 d->status = DimHalf;
961 POLLER_CALL(d->pollLoader->poller(), catchIdleEvent());
962 float newBrightness = Solid::Control::PowerManager::brightness() / 2;
963 Solid::Control::PowerManager::setBrightness(newBrightness);
965 } else {
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);
983 } else {
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);
990 } else {
991 nextTimeout = minDimEvent - idle;
995 if (nextTimeout >= 0) {
996 POLLER_CALL(d->pollLoader->poller(), setNextTimeout(nextTimeout * 1000));
997 kDebug() << "Next timeout in" << nextTimeout << "seconds";
998 } else {
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 */
1014 if (!slot) {
1015 KNotification::event(evid, message, KIcon(iconname).pixmap(20, 20),
1016 0, KNotification::CloseOnTimeout, d->applicationData);
1017 } else {
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()) {
1035 if (slot) {
1036 QTimer::singleShot(0, this, slot);
1038 return;
1041 if (!slot) {
1042 KNotification::event(evid, message, KIcon(iconname).pixmap(20, 20),
1043 0, KNotification::CloseOnTimeout, d->applicationData);
1044 } else {
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()) {
1062 if (slot) {
1063 QTimer::singleShot(0, this, slot);
1065 return;
1068 if (!slot) {
1069 KNotification::event(evid, message, KIcon(iconname).pixmap(20, 20),
1070 0, KNotification::CloseOnTimeout, d->applicationData);
1071 } else {
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));
1123 reloadProfile();
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());
1135 } else {
1136 if (state == -1) {
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());
1146 } else {
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));
1158 } else {
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();
1172 reloadAndStream();
1174 return;
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
1186 * to be loaded!!
1189 applyProfile();
1191 KConfigGroup * settings = getCurrentProfile();
1193 emitNotification("profileset", i18n("Profile changed to \"%1\"", profile),
1194 0, settings->readEntry("iconname"));
1197 void PowerDevilDaemon::reloadAndStream()
1199 reloadProfile();
1201 setAvailableProfiles(d->profilesConfig->groupList());
1203 streamData();
1205 refreshStatus();
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;
1240 return retlist;
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;
1261 return retlist;
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) {
1282 case S2Disk:
1283 QTimer::singleShot(100, this, SLOT(suspendToDisk()));
1284 break;
1285 case S2Ram:
1286 QTimer::singleShot(100, this, SLOT(suspendToRam()));
1287 break;
1288 case Standby:
1289 QTimer::singleShot(100, this, SLOT(standby()));
1290 break;
1291 default:
1292 break;
1296 void PowerDevilDaemon::setBrightness(int value)
1298 if (value == -2) {
1299 // Then set brightness to half the current brightness.
1301 float b = Solid::Control::PowerManager::brightness() / 2;
1302 Solid::Control::PowerManager::setBrightness(b);
1303 return;
1306 Solid::Control::PowerManager::setBrightness(value);
1309 void PowerDevilDaemon::turnOffScreen()
1311 #ifdef HAVE_DPMS
1313 CARD16 dummy;
1314 BOOL enabled;
1315 Display *dpy = QX11Info::display();
1317 DPMSInfo(dpy, &dummy, &enabled);
1319 if (enabled) {
1320 DPMSForceLevel(dpy, DPMSModeOff);
1321 } else {
1322 DPMSEnable(dpy);
1323 DPMSForceLevel(dpy, DPMSModeOff);
1325 #endif
1328 void PowerDevilDaemon::profileFirstLoad()
1330 KConfigGroup * settings = getCurrentProfile();
1332 if (!settings)
1333 return;
1335 kDebug() << "Profile initialization";
1337 if (!settings->readEntry("scriptpath", QString()).isEmpty()) {
1338 QProcess::startDetached(settings->readEntry("scriptpath"));
1341 // Compositing!!
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",
1369 "org.kde.KWin",
1370 "reloadConfig");
1371 QDBusConnection::sessionBus().send(message);
1373 return true;
1374 } else {
1375 return false;
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(&copyTo);
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;
1411 profileFirstLoad();
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"