1
/* This file is part of the KDE project
2
Copyright (C) 2006 Kevin Ottens <ervin@kde.org>
3
Copyright (C) 2008-2010 Dario Freddi <drf@kde.org>
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License version 2 as published by the Free Software Foundation.
9
This library 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 GNU
12
Library General Public License for more details.
14
You should have received a copy of the GNU Library General Public License
15
along with this library; see the file COPYING.LIB. If not, write to
16
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
Boston, MA 02110-1301, USA.
22
#include "powerdevilhalbackend.h"
23
#include <Solid/DeviceNotifier>
24
#include <QtDBus/QDBusReply>
25
#include <QtDBus/QDBusConnectionInterface>
27
#include "halsuspendjob.h"
28
#include <Solid/Device>
29
#include <Solid/Button>
30
#include <Solid/Battery>
31
#include <Solid/GenericInterface>
32
#include <Solid/AcAdapter>
33
#include <KPluginFactory>
34
#include <QtCore/QTimer>
36
PowerDevilHALBackend::PowerDevilHALBackend(QObject* parent)
37
: BackendInterface(parent),
38
m_brightnessInHardware(false),
39
m_halComputer("org.freedesktop.Hal",
40
"/org/freedesktop/Hal/devices/computer",
41
"org.freedesktop.Hal.Device",
42
QDBusConnection::systemBus()),
43
m_halPowerManagement("org.freedesktop.Hal",
44
"/org/freedesktop/Hal/devices/computer",
45
"org.freedesktop.Hal.Device.SystemPowerManagement",
46
QDBusConnection::systemBus()),
47
m_halCpuFreq("org.freedesktop.Hal",
48
"/org/freedesktop/Hal/devices/computer",
49
"org.freedesktop.Hal.Device.CPUFreq",
50
QDBusConnection::systemBus()),
51
m_halManager("org.freedesktop.Hal",
52
"/org/freedesktop/Hal/Manager",
53
"org.freedesktop.Hal.Manager",
54
QDBusConnection::systemBus())
58
PowerDevilHALBackend::~PowerDevilHALBackend()
60
qDeleteAll(m_acAdapters);
61
qDeleteAll(m_batteries);
62
qDeleteAll(m_buttons);
65
bool PowerDevilHALBackend::isAvailable()
67
return QDBusConnection::systemBus().interface()->isServiceRegistered("org.freedesktop.Hal");
70
void PowerDevilHALBackend::init()
72
setCapabilities(NoCapabilities);
74
connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(const QString &)),
75
this, SLOT(slotDeviceRemoved(const QString &)));
76
connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(const QString &)),
77
this, SLOT(slotDeviceAdded(const QString &)));
79
m_pluggedAdapterCount = 0;
87
// Brightness Control available
88
BrightnessControlsList controls;
90
QDBusPendingReply<QStringList> reply = m_halManager.asyncCall("FindDeviceByCapability", "laptop_panel");
91
reply.waitForFinished();
92
if (reply.isValid()) {
93
foreach(const QString &name, reply.value()) {
94
controls.insert(name, Screen);
97
reply = m_halManager.call("FindDeviceByCapability", "keyboard_backlight");
98
if (reply.isValid()) {
99
foreach(const QString &name, reply.value()) {
100
controls.insert(name, Keyboard);
105
QList<QString> screenControls = controls.keys(Screen);
107
if (!screenControls.isEmpty()) {
108
m_cachedBrightness = brightness(Screen);
110
QDBusInterface deviceInterface("org.freedesktop.Hal",
111
screenControls.at(0),
112
"org.freedesktop.Hal.Device",
113
QDBusConnection::systemBus());
114
QDBusReply<bool> replyInHardware = deviceInterface.call("GetPropertyBoolean",
115
"laptop_panel.brightness_in_hardware");
116
if (replyInHardware.isValid()) {
117
m_brightnessInHardware = replyInHardware;
121
// Supported suspend methods
122
SuspendMethods supported = UnknownSuspendMethod;
124
QDBusPendingReply<bool> reply = m_halComputer.asyncCall("GetPropertyBoolean", "power_management.can_suspend");
125
reply.waitForFinished();
127
if (reply.isValid()) {
128
bool can_suspend = reply;
133
kDebug() << reply.error().name() << ": " << reply.error().message();
136
reply = m_halComputer.asyncCall("GetPropertyBoolean", "power_management.can_hibernate");
137
reply.waitForFinished();
139
if (reply.isValid()) {
140
bool can_hibernate = reply;
145
kDebug() << reply.error().name() << ": " << reply.error().message();
151
foreach (Solid::Device *d, m_batteries) {
152
Solid::GenericInterface *interface = d->as<Solid::GenericInterface>();
154
if (interface == 0) continue;
156
if (interface->property("battery.reporting.design").toInt() > 0) {
157
uint capacity = ((float)(interface->property("battery.reporting.last_full").toInt()) /
158
(float)(interface->property("battery.reporting.design").toInt())) * 100;
161
setCapacityForBattery(d->udi(), capacity);
163
// Not supported, set capacity to 100%
164
setCapacityForBattery(d->udi(), 100);
167
// Not supported, set capacity to 100%
168
setCapacityForBattery(d->udi(), 100);
173
setBackendIsReady(controls, supported);
176
void PowerDevilHALBackend::brightnessKeyPressed(PowerDevil::BackendInterface::BrightnessKeyType type)
178
BrightnessControlsList controls = brightnessControlsAvailable();
179
QList<QString> screenControls = controls.keys(Screen);
181
if (screenControls.isEmpty()) {
182
return; // ignore as we are not able to determine the brightness level
185
float currentBrightness = brightness(Screen);
187
if (qFuzzyCompare(currentBrightness, m_cachedBrightness) && !m_brightnessInHardware) {
189
if (type == Increase) {
190
newBrightness = qMin(100.0f, currentBrightness + 10);
192
newBrightness = qMax(0.0f, currentBrightness - 10);
195
if (setBrightness(newBrightness, Screen)) {
196
newBrightness = brightness(Screen);
197
if (!qFuzzyCompare(newBrightness, m_cachedBrightness)) {
198
m_cachedBrightness = newBrightness;
199
onBrightnessChanged(Screen, m_cachedBrightness);
203
m_cachedBrightness = currentBrightness;
207
float PowerDevilHALBackend::brightness(PowerDevil::BackendInterface::BrightnessControlType type) const
210
if (type == Screen) {
211
QDBusPendingReply<QStringList> reply = m_halManager.asyncCall("FindDeviceByCapability", "laptop_panel");
212
reply.waitForFinished();
213
if(reply.isValid()) {
214
foreach (const QString &device, reply.value()) {
215
QDBusInterface deviceInterface("org.freedesktop.Hal", device,
216
"org.freedesktop.Hal.Device.LaptopPanel", QDBusConnection::systemBus());
217
QDBusReply<int> brightnessReply = deviceInterface.call("GetBrightness");
218
if(!brightnessReply.isValid()) {
221
brightness = brightnessReply;
223
QDBusInterface propertyInterface("org.freedesktop.Hal", device,
224
"org.freedesktop.Hal.Device", QDBusConnection::systemBus());
225
QDBusReply<int> levelsReply = propertyInterface.call("GetProperty", "laptop_panel.num_levels");
226
if (levelsReply.isValid()) {
227
int levels = levelsReply;
228
return (float)(100*(brightness/(levels-1)));
233
QDBusPendingReply<QStringList> reply = m_halManager.asyncCall("FindDeviceByCapability", "keyboard_backlight");
234
reply.waitForFinished();
235
if(reply.isValid()) {
236
foreach (const QString &device, reply.value()) {
237
//TODO - I do not have a backlight enabled keyboard, so I'm guessing a bit here. Could someone please check this.
238
QDBusInterface deviceInterface("org.freedesktop.Hal", device,
239
"org.freedesktop.Hal.Device.KeyboardBacklight", QDBusConnection::systemBus());
241
QDBusReply<int> brightnessReply = deviceInterface.call("GetBrightness");
242
if (!brightnessReply.isValid()) {
245
brightness = brightnessReply;
247
QDBusInterface propertyInterface("org.freedesktop.Hal", device,
248
"org.freedesktop.Hal.Device", QDBusConnection::systemBus());
249
QDBusReply<int> levelsReply = propertyInterface.call("GetProperty", "keyboard_backlight.num_levels");
250
if (levelsReply.isValid()) {
251
int levels = levelsReply;
252
return (float)(100*(brightness/(levels-1)));
260
bool PowerDevilHALBackend::setBrightness(float brightnessValue, PowerDevil::BackendInterface::BrightnessControlType type)
262
if (type == Screen) {
263
QDBusPendingReply<QStringList> reply = m_halManager.asyncCall("FindDeviceByCapability", "laptop_panel");
264
reply.waitForFinished();
265
if(reply.isValid()) {
266
foreach (const QString &device, reply.value()) {
267
QDBusInterface propertyInterface("org.freedesktop.Hal", device,
268
"org.freedesktop.Hal.Device", QDBusConnection::systemBus());
269
int levels = propertyInterface.call("GetProperty", "laptop_panel.num_levels").arguments().at(0).toInt();
270
QDBusInterface deviceInterface("org.freedesktop.Hal", device,
271
"org.freedesktop.Hal.Device.LaptopPanel", QDBusConnection::systemBus());
272
deviceInterface.call("SetBrightness", qRound((levels-1)*(brightnessValue/100.0))); // .0? The right way? Feels hackish.
273
if (!deviceInterface.lastError().isValid()) {
279
QDBusPendingReply<QStringList> reply = m_halManager.asyncCall("FindDeviceByCapability", "keyboard_backlight");
280
reply.waitForFinished();
281
if(reply.isValid()) {
282
foreach (const QString &device, reply.value()) {
283
QDBusInterface propertyInterface("org.freedesktop.Hal", device,
284
"org.freedesktop.Hal.Device", QDBusConnection::systemBus());
285
int levels = propertyInterface.call("GetProperty", "keyboard_backlight.num_levels").arguments().at(0).toInt();
286
//TODO - I do not have a backlight enabled keyboard, so I'm guessing a bit here. Could someone please check this.
287
QDBusInterface deviceInterface("org.freedesktop.Hal", device,
288
"org.freedesktop.Hal.Device.KeyboardBacklight", QDBusConnection::systemBus());
290
deviceInterface.call("SetBrightness", qRound((levels-1)*(brightnessValue/100.0)));
291
if(!deviceInterface.lastError().isValid()) {
300
KJob* PowerDevilHALBackend::suspend(PowerDevil::BackendInterface::SuspendMethod method)
302
// Ok, that's not cool, but it's all HAL really gives us, so.
303
QTimer::singleShot(0, this, SLOT(setResumeFromSuspend()));
304
return new HalSuspendJob(m_halPowerManagement, m_halComputer,
305
method, supportedSuspendMethods());
308
void PowerDevilHALBackend::computeAcAdapters()
310
QList<Solid::Device> adapters
311
= Solid::Device::listFromType(Solid::DeviceInterface::AcAdapter);
313
foreach (const Solid::Device &adapter, adapters) {
314
m_acAdapters[adapter.udi()] = new Solid::Device(adapter);
315
connect(m_acAdapters[adapter.udi()]->as<Solid::AcAdapter>(), SIGNAL(plugStateChanged(bool, const QString &)),
316
this, SLOT(slotPlugStateChanged(bool)));
318
if (m_acAdapters[adapter.udi()]->as<Solid::AcAdapter>()!=0
319
&& m_acAdapters[adapter.udi()]->as<Solid::AcAdapter>()->isPlugged()) {
320
m_pluggedAdapterCount++;
324
if (m_pluggedAdapterCount > 0) {
325
setAcAdapterState(Plugged);
327
setAcAdapterState(Unplugged);
331
void PowerDevilHALBackend::computeBatteries()
333
QList<Solid::Device> batteries
334
= Solid::Device::listFromQuery("Battery.type == 'PrimaryBattery'");
336
foreach (const Solid::Device &battery, batteries) {
337
m_batteries[battery.udi()] = new Solid::Device(battery);
338
connect(m_batteries[battery.udi()]->as<Solid::Battery>(), SIGNAL(chargePercentChanged(int, const QString &)),
339
this, SLOT(updateBatteryStats()));
340
connect(m_batteries[battery.udi()]->as<Solid::GenericInterface>(), SIGNAL(propertyChanged(const QMap<QString,int> &)),
341
this, SLOT(slotBatteryPropertyChanged(const QMap<QString,int> &)));
344
updateBatteryStats();
347
void PowerDevilHALBackend::computeButtons()
349
QList<Solid::Device> buttons
350
= Solid::Device::listFromType(Solid::DeviceInterface::Button);
352
foreach (const Solid::Device &button, buttons) {
353
m_buttons[button.udi()] = new Solid::Device(button);
354
connect(m_buttons[button.udi()]->as<Solid::Button>(), SIGNAL(pressed(Solid::Button::ButtonType, const QString &)),
355
this, SLOT(slotButtonPressed(Solid::Button::ButtonType)));
359
void PowerDevilHALBackend::slotPlugStateChanged(bool newState)
362
if(m_pluggedAdapterCount == 0) {
363
setAcAdapterState(Plugged);
365
m_pluggedAdapterCount++;
367
if(m_pluggedAdapterCount == 1) {
368
setAcAdapterState(Unplugged);
370
m_pluggedAdapterCount--;
374
void PowerDevilHALBackend::slotButtonPressed(Solid::Button::ButtonType type)
376
Solid::Button *button = qobject_cast<Solid::Button *>(sender());
378
if (button == 0) return;
381
case Solid::Button::PowerButton:
382
emit buttonPressed(PowerButton);
384
case Solid::Button::SleepButton:
385
emit buttonPressed(SleepButton);
387
case Solid::Button::LidButton:
388
if (button->stateValue()) {
389
emit buttonPressed(LidClose);
391
emit buttonPressed(LidOpen);
395
//kWarning() << "Unknown button type";
400
void PowerDevilHALBackend::slotDeviceAdded(const QString &udi)
402
Solid::Device *device = new Solid::Device(udi);
403
if (device->is<Solid::AcAdapter>()) {
404
m_acAdapters[udi] = device;
405
connect(m_acAdapters[udi]->as<Solid::AcAdapter>(), SIGNAL(plugStateChanged(bool, const QString &)),
406
this, SLOT(slotPlugStateChanged(bool)));
408
if (m_acAdapters[udi]->as<Solid::AcAdapter>()!=0
409
&& m_acAdapters[udi]->as<Solid::AcAdapter>()->isPlugged()) {
410
m_pluggedAdapterCount++;
412
} else if (device->is<Solid::Battery>()) {
413
m_batteries[udi] = device;
414
connect(m_batteries[udi]->as<Solid::Battery>(), SIGNAL(chargePercentChanged(int, const QString &)),
415
this, SLOT(updateBatteryStats()));
416
connect(m_batteries[udi]->as<Solid::GenericInterface>(), SIGNAL(propertyChanged(const QMap<QString,int> &)),
417
this, SLOT(slotBatteryPropertyChanged(const QMap<QString,int> &)));
418
} else if (device->is<Solid::Button>()) {
419
m_buttons[udi] = device;
420
connect(m_buttons[udi]->as<Solid::Button>(), SIGNAL(pressed(int, const QString &)),
421
this, SLOT(slotButtonPressed(int)));
427
void PowerDevilHALBackend::slotDeviceRemoved(const QString &udi)
429
Solid::Device *device = 0;
431
device = m_acAdapters.take(udi);
436
m_pluggedAdapterCount = 0;
438
foreach (Solid::Device *d, m_acAdapters) {
439
if (d->as<Solid::AcAdapter>()!=0
440
&& d->as<Solid::AcAdapter>()->isPlugged()) {
441
m_pluggedAdapterCount++;
448
device = m_batteries.take(udi);
452
updateBatteryStats();
456
device = m_buttons.take(udi);
464
void PowerDevilHALBackend::slotBatteryPropertyChanged(const QMap<QString,int> &changes)
466
/* This slot catches property changes on battery devices. At
467
* the moment it is used to find out remaining time on batteries.
470
if (changes.contains("battery.remaining_time")) {
471
updateBatteryStats();
473
setBatteryRemainingTime(m_estimatedBatteryTime);
477
void PowerDevilHALBackend::updateBatteryStats()
479
m_currentBatteryCharge = 0;
480
m_maxBatteryCharge = 0;
481
m_warningBatteryCharge = 0;
482
m_lowBatteryCharge = 0;
483
m_criticalBatteryCharge = 0;
484
m_estimatedBatteryTime = 0;
486
foreach (Solid::Device *d, m_batteries) {
487
Solid::GenericInterface *interface = d->as<Solid::GenericInterface>();
489
if (interface == 0) continue;
491
m_currentBatteryCharge += interface->property("battery.charge_level.current").toInt();
492
m_maxBatteryCharge += interface->property("battery.charge_level.last_full").toInt();
493
m_warningBatteryCharge += interface->property("battery.charge_level.warning").toInt();
494
m_lowBatteryCharge += interface->property("battery.charge_level.low").toInt();
495
m_estimatedBatteryTime += interface->property("battery.remaining_time").toInt() * 1000;
498
m_criticalBatteryCharge = m_lowBatteryCharge/2;
501
#include "powerdevilhalbackend.moc"