Renames & Add new properties to QBatteryInfo
[qt:qtsystems.git] / src / systeminfo / windows / qbatteryinfo_win.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
5 ** Contact: http://www.qt-project.org/legal
6 **
7 ** This file is part of the QtSystems module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Digia.  For licensing terms and
15 ** conditions see http://qt.digia.com/licensing.  For further information
16 ** use the contact form at http://qt.digia.com/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 2.1 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL included in the
22 ** packaging of this file.  Please review the following information to
23 ** ensure the GNU Lesser General Public License version 2.1 requirements
24 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** In addition, as a special exception, Digia gives you certain additional
27 ** rights.  These rights are described in the Digia Qt LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qbatteryinfo_win_p.h"
44
45 #include <qt_windows.h>
46
47 #include <powrprof.h>
48 #include <setupapi.h>
49
50 #if !defined (Q_CC_MINGW) || defined(__MINGW64_VERSION_MAJOR)
51 #  include <BatClass.h>
52 #endif
53
54 #include <QtCore/qmetaobject.h>
55 #include <QtCore/QTimer>
56 #include <QtCore/QUuid>
57 #include <QtCore/qnumeric.h>
58
59 #ifdef Q_CC_MSVC
60 #  pragma comment (lib, "Setupapi.lib")
61 #endif
62
63 QT_BEGIN_NAMESPACE
64
65 QBatteryInfoPrivate::QBatteryInfoPrivate(QBatteryInfo *parent)
66     : QObject(parent)
67     , q_ptr(parent)
68     , timeToFull(0)
69     , numberOfBatteries(0)
70     , index(0)
71 {
72     initialize();
73 }
74
75 QBatteryInfoPrivate::QBatteryInfoPrivate(int batteryIndex, QBatteryInfo *parent)
76     : QObject(parent)
77     , q_ptr(parent)
78     , timeToFull(0)
79     , numberOfBatteries(0)
80     , index(batteryIndex)
81 {
82     initialize();
83 }
84
85 void QBatteryInfoPrivate::initialize()
86 {
87     QTimer *timer = new QTimer(this);
88     connect(timer,SIGNAL(timeout()),this,SLOT(getBatteryStatus()));
89     timer->start(1000);
90     getBatteryStatus();
91 }
92
93 QBatteryInfoPrivate::~QBatteryInfoPrivate()
94 {
95 }
96
97 int QBatteryInfoPrivate::batteryCount()
98 {
99     return numberOfBatteries;
100 }
101
102 int QBatteryInfoPrivate::batteryIndex() const
103 {
104     return index;
105 }
106
107 bool QBatteryInfoPrivate::isValid()
108 {
109     // valid if the index < total count.
110     return (index >= 0) && (index < batteryCount());
111 }
112
113 void QBatteryInfoPrivate::setBatteryIndex(int batteryIndex)
114 {
115     if (index != batteryIndex) {
116         bool validBefore = isValid();
117         int oldIndex = index;
118         index = batteryIndex;
119         bool validNow = isValid();
120         if (validBefore != validNow)
121             Q_EMIT validChanged(validNow);
122
123         if (validNow) {
124             if (validBefore) {
125                 // valid now, valid before so we have to check individual values
126
127                 // ignore chargerType - it won't change based on battery index
128                 //emit chargerTypeChanged(newChargerType);
129
130                 QBatteryInfo::ChargingState newChargingState = chargingState();
131                 if (newChargingState != chargingState(oldIndex))
132                     emit chargingStateChanged(newChargingState);
133
134                 int newValue = level();
135                 if (newValue != level(oldIndex))
136                     emit levelChanged(newValue);
137
138                 newValue = currentFlow();
139                 if (newValue != currentFlow(oldIndex))
140                     emit currentFlowChanged(newValue);
141
142                 newValue = cycleCount();
143                 if (newValue != cycleCount(oldIndex))
144                     emit cycleCountChanged(newValue);
145
146                 newValue = remainingCapacity();
147                 if (newValue != remainingCapacity(oldIndex))
148                     emit remainingCapacityChanged(newValue);
149
150                 newValue = remainingChargingTime();
151                 if (newValue != remainingChargingTime(oldIndex))
152                     emit remainingChargingTimeChanged(newValue);
153
154                 newValue = voltage();
155                 if (newValue != voltage(oldIndex))
156                     emit voltageChanged(newValue);
157
158                 QBatteryInfo::LevelStatus newLevelStatus = levelStatus();
159                 if (newLevelStatus != levelStatus(oldIndex))
160                     emit levelStatusChanged(newLevelStatus);
161
162                 QBatteryInfo::Health newHealth = health();
163                 if (newHealth != health(oldIndex))
164                     emit healthChanged(newHealth);
165
166                 float newTemperature = temperature();
167                 if (!qFuzzyCompare(newTemperature, temperature(oldIndex)))
168                     emit temperatureChanged(newTemperature);
169             } else {
170                 // it wasn't valid before so everything is changed
171
172                 // ignore chargerType - it won't change based on battery index
173                 //emit chargerTypeChanged(newChargerType);
174
175                 emit chargingStateChanged(chargingState());
176                 emit levelChanged(level());
177                 emit currentFlowChanged(currentFlow());
178                 emit cycleCountChanged(cycleCount());
179                 emit remainingCapacityChanged(remainingCapacity());
180                 emit remainingChargingTimeChanged(remainingChargingTime());
181                 emit voltageChanged(voltage());
182                 emit levelStatusChanged(levelStatus());
183                 emit healthChanged(health());
184                 emit temperatureChanged(temperature());
185             }
186         }
187
188         emit batteryIndexChanged(index);
189     }
190 }
191
192 int QBatteryInfoPrivate::level(int battery)
193 {
194     int maxCapacity = maximumCapacity(battery);
195     int remCapacity = remainingCapacity(battery);
196
197     if (maxCapacity == 0)
198         return -1;
199
200     return remCapacity * 100 / maxCapacity;
201 }
202
203 int QBatteryInfoPrivate::level()
204 {
205     return level(index);
206 }
207
208 int QBatteryInfoPrivate::currentFlow(int battery)
209 {
210     return currentFlows[battery];
211 }
212
213 int QBatteryInfoPrivate::currentFlow()
214 {
215     return currentFlow(index);
216 }
217
218 int QBatteryInfoPrivate::cycleCount(int battery)
219 {
220     Q_UNUSED(battery)
221
222     return -1;
223 }
224
225 int QBatteryInfoPrivate::cycleCount()
226 {
227     return cycleCount(index);
228 }
229
230 int QBatteryInfoPrivate::maximumCapacity(int battery)
231 {
232     return maximumCapacities[battery];
233 }
234
235 int QBatteryInfoPrivate::maximumCapacity()
236 {
237     return maximumCapacity(index);
238 }
239
240 int QBatteryInfoPrivate::remainingCapacity(int battery)
241 {
242     return remainingCapacities[battery];
243 }
244
245 int QBatteryInfoPrivate::remainingCapacity()
246 {
247     return remainingCapacity(index);
248 }
249
250 int QBatteryInfoPrivate::remainingChargingTime(int battery)
251 {
252     Q_UNUSED(battery)
253     SYSTEM_BATTERY_STATE systemBatteryState;
254     CallNtPowerInformation(SystemBatteryState,NULL,0,&systemBatteryState,sizeof(systemBatteryState));
255
256    int cTime = systemBatteryState.EstimatedTime;
257     if (cTime != timeToFull) {
258         timeToFull = cTime;
259         emit remainingChargingTimeChanged(timeToFull);
260     }
261
262     return timeToFull;
263 }
264
265 int QBatteryInfoPrivate::remainingChargingTime()
266 {
267     return remainingChargingTime(index);
268 }
269
270 int QBatteryInfoPrivate::voltage(int battery)
271 {
272     return voltages[battery];
273 }
274
275 int QBatteryInfoPrivate::voltage()
276 {
277     return voltage(index);
278 }
279
280 QBatteryInfo::ChargerType QBatteryInfoPrivate::chargerType()
281 {
282     return currentChargerType;
283 }
284
285 QBatteryInfo::ChargingState QBatteryInfoPrivate::chargingState(int battery)
286 {
287     return chargingStates[battery];
288 }
289
290 QBatteryInfo::ChargingState QBatteryInfoPrivate::chargingState()
291 {
292     return chargingState(index);
293 }
294
295 QBatteryInfo::LevelStatus QBatteryInfoPrivate::levelStatus(int battery)
296 {
297     return levelStatuss[battery];
298 }
299
300 QBatteryInfo::LevelStatus QBatteryInfoPrivate::levelStatus()
301 {
302     return levelStatus(index);
303 }
304
305 QBatteryInfo::Health QBatteryInfoPrivate::health(int battery)
306 {
307     Q_UNUSED(battery)
308
309     return QBatteryInfo::HealthUnknown;
310 }
311
312 QBatteryInfo::Health QBatteryInfoPrivate::health()
313 {
314     return health(index);
315 }
316
317 float QBatteryInfoPrivate::temperature(int battery)
318 {
319     Q_UNUSED(battery)
320
321     return qQNaN();
322 }
323
324 float QBatteryInfoPrivate::temperature()
325 {
326     return temperature(index);
327 }
328
329 void QBatteryInfoPrivate::getBatteryStatus()
330 {
331 #if !defined (Q_CC_MINGW) || defined(__MINGW64_VERSION_MAJOR)
332     SYSTEM_BATTERY_STATE systemBatteryState;
333     CallNtPowerInformation(SystemBatteryState,NULL,0,&systemBatteryState,sizeof(systemBatteryState));
334
335     int cTime = systemBatteryState.EstimatedTime;
336     if (cTime != timeToFull) {
337         timeToFull = cTime;
338         emit remainingChargingTimeChanged(timeToFull);
339     }
340
341     int batteryNumber = 0;
342
343     QUuid guidDeviceBattery(0x72631e54L,0x78A4,0x11d0,0xbc,0xf7,0x00,0xaa,0x00,0xb7,0xb3,0x2a);
344     GUID GUID_DEVICE_BATTERY = static_cast<GUID>(guidDeviceBattery);
345
346     HDEVINFO hdevInfo = SetupDiGetClassDevs(&GUID_DEVICE_BATTERY,0,0,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
347     if (INVALID_HANDLE_VALUE != hdevInfo) {
348         for (int i = 0; i < 100; i++) {
349             SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0};
350             deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
351
352             if (SetupDiEnumDeviceInterfaces(hdevInfo,0,&GUID_DEVICE_BATTERY,i,&deviceInterfaceData)){
353                 DWORD cbRequired = 0;
354
355                 SetupDiGetDeviceInterfaceDetail(hdevInfo, &deviceInterfaceData,0, 0, &cbRequired, 0);
356                 if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
357                     PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetail =
358                             (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
359                     if (deviceInterfaceDetail){
360                         deviceInterfaceDetail->cbSize = sizeof(*deviceInterfaceDetail);
361                         if (SetupDiGetDeviceInterfaceDetail(hdevInfo, &deviceInterfaceData, deviceInterfaceDetail, cbRequired, &cbRequired, 0)) {
362
363                             batteryNumber++; //new battery
364                             HANDLE batteryHandle = CreateFile(deviceInterfaceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
365                                                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
366                             if (INVALID_HANDLE_VALUE != batteryHandle){
367
368                                 BATTERY_QUERY_INFORMATION batteryQueryInfo = {0};
369
370                                 DWORD inBuf = 0;
371                                 DWORD dwOut;
372
373                                 if (DeviceIoControl(batteryHandle,IOCTL_BATTERY_QUERY_TAG, &inBuf, sizeof(inBuf), &batteryQueryInfo.BatteryTag, sizeof(batteryQueryInfo.BatteryTag), &dwOut, NULL)
374                                         && batteryQueryInfo.BatteryTag) {
375
376                                     BATTERY_INFORMATION batteryInfo = {0};
377                                     batteryQueryInfo.InformationLevel = BatteryInformation;
378
379                                     if (DeviceIoControl(batteryHandle, IOCTL_BATTERY_QUERY_INFORMATION,
380                                                         &batteryQueryInfo, sizeof(batteryQueryInfo),
381                                                         &batteryInfo, sizeof(batteryInfo), &dwOut, NULL)) {
382
383                                         maximumCapacities.insert(batteryNumber, -1);
384
385                                         if (batteryInfo.Capabilities & BATTERY_SYSTEM_BATTERY) {
386                                             if (!(batteryInfo.Capabilities & BATTERY_IS_SHORT_TERM)) {
387  //                                                dwResult |= HASBATTERY;
388
389                                                 BATTERY_WAIT_STATUS batteryWaitStatus = {0};
390                                                 batteryWaitStatus.BatteryTag = batteryQueryInfo.BatteryTag;
391
392                                                 BATTERY_STATUS batteryStatus;
393                                                 if (DeviceIoControl(batteryHandle, IOCTL_BATTERY_QUERY_STATUS,
394                                                                     &batteryWaitStatus, sizeof(batteryWaitStatus),
395                                                                     &batteryStatus, sizeof(batteryStatus), &dwOut, NULL)) {
396
397                                                     QBatteryInfo::ChargerType chargerType = QBatteryInfo::UnknownCharger;
398
399                                                     if (batteryStatus.PowerState & BATTERY_POWER_ON_LINE) {
400                                                         chargerType = QBatteryInfo::WallCharger;
401                                                     }
402                                                     if (currentChargerType != chargerType) {
403                                                         currentChargerType = chargerType;
404                                                         Q_EMIT chargerTypeChanged(chargerType);
405                                                     }
406
407                                                     QBatteryInfo::ChargingState chargingState;
408                                                     if (batteryStatus.PowerState & BATTERY_CHARGING)
409                                                         chargingState = QBatteryInfo::Charging;
410                                                     if (batteryStatus.PowerState & BATTERY_DISCHARGING)
411                                                          chargingState = QBatteryInfo::Discharging;
412
413                                                     if (chargingStates[batteryNumber] != chargingState) {
414                                                         chargingStates.insert(batteryNumber, chargingState);
415                                                         if (batteryNumber == index)
416                                                             emit chargingStateChanged(chargingState);
417                                                     }
418
419                                                     if (voltages[batteryNumber] !=  batteryStatus.Voltage) {
420                                                         voltages.insert(batteryNumber, batteryStatus.Voltage);
421                                                         if (batteryNumber == index)
422                                                             Q_EMIT voltageChanged(batteryStatus.Voltage);
423                                                     }
424                                                     if (currentFlows[batteryNumber] != batteryStatus.Rate) {
425                                                         currentFlows.insert(batteryNumber,batteryStatus.Rate);
426                                                         if (batteryNumber == index)
427                                                             Q_EMIT currentFlowChanged(batteryStatus.Rate);
428                                                     }
429                                                     if (batteryStatus.Voltage == BATTERY_UNKNOWN_VOLTAGE) {
430                                                         // If we don't have the voltage then we can't convert from mWh to mAh
431                                                         batteryStatus.Capacity = -1;
432                                                         maximumCapacities.insert(batteryNumber, -1);
433                                                     } else {
434                                                         // Convert from mWh to mAh
435                                                         batteryStatus.Capacity = batteryStatus.Capacity / batteryStatus.Voltage;
436                                                         maximumCapacities.insert(batteryNumber, batteryInfo.FullChargedCapacity / batteryStatus.Voltage);
437                                                     }
438                                                     if (remainingCapacities[batteryNumber] != batteryStatus.Capacity) {
439                                                         remainingCapacities.insert(batteryNumber, batteryStatus.Capacity);
440                                                         if (batteryNumber == index)
441                                                             Q_EMIT remainingCapacityChanged(batteryStatus.Capacity);
442                                                     }
443                                                     ///
444                                                     int level = batteryInfo.FullChargedCapacity / batteryStatus.Capacity;
445                                                     QBatteryInfo::LevelStatus batStatus = QBatteryInfo::LevelUnknown;
446
447                                                     if (batteryStatus.PowerState & BATTERY_CRITICAL) {
448                                                         batStatus =QBatteryInfo::LevelEmpty ;
449                                                     } else if (level < 67) {
450                                                         batStatus = QBatteryInfo::LevelLow;
451                                                     } else if (level > 66) {
452                                                        batStatus = QBatteryInfo::LevelOk;
453                                                     } else if (level == 100) {
454                                                         batStatus = QBatteryInfo::LevelFull;
455                                                     }
456
457                                                     if (levelStatuss[batteryNumber] != batStatus) {
458                                                         levelStatuss.insert(batteryNumber,batStatus);
459                                                         if (batteryNumber == index)
460                                                             Q_EMIT levelStatusChanged(batStatus);
461                                                     }
462                                                 }
463                                             }
464                                         }
465                                     } //end IOCTL_BATTERY_QUERY_INFORMATION
466                                 } // end BATTERY_INFORMATION
467
468                                 CloseHandle(batteryHandle);
469                             }
470                         }
471                         LocalFree(deviceInterfaceDetail);
472                     }
473                 }
474             }
475             else  if (ERROR_NO_MORE_ITEMS == GetLastError()) {
476                 break;
477             }
478         }
479         SetupDiDestroyDeviceInfoList(hdevInfo);
480     }
481     bool validBefore = isValid();
482     numberOfBatteries = batteryNumber;
483     bool validNow = isValid();
484     if (validBefore != validNow)
485         Q_EMIT validChanged(validNow);
486
487 #else // !defined (Q_CC_MINGW) || defined(__MINGW64_VERSION_MAJOR)
488     numberOfBatteries = 0;
489     Q_UNIMPLEMENTED();
490 #endif
491 }
492
493 QT_END_NAMESPACE