File indexing completed on 2024-05-26 04:05:39

0001 /*
0002     SPDX-FileCopyrightText: 2013 Patrick von Reth <vonreth@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "winbattery.h"
0007 #include "windevicemanager_p.h"
0008 
0009 #include <batclass.h>
0010 #include <devguid.h>
0011 #include <devpropdef.h>
0012 #include <setupapi.h>
0013 
0014 using namespace Solid::Backends::Win;
0015 
0016 QMap<QString, WinBattery::Battery> WinBattery::m_udiToGDI = QMap<QString, WinBattery::Battery>();
0017 
0018 WinBattery::WinBattery(WinDevice *device)
0019     : WinInterface(device)
0020     , m_state(Solid::Battery::NoCharge)
0021 {
0022     powerChanged();
0023     connect(SolidWinEventFilter::instance(), SIGNAL(powerChanged()), this, SLOT(powerChanged()));
0024 }
0025 
0026 Solid::Battery::BatteryType WinBattery::type() const
0027 {
0028     return m_type;
0029 }
0030 
0031 int WinBattery::chargePercent() const
0032 {
0033     return m_charge;
0034 }
0035 
0036 int WinBattery::capacity() const
0037 {
0038     return m_capacity;
0039 }
0040 
0041 bool WinBattery::isRechargeable() const
0042 {
0043     return m_rechargeable;
0044 }
0045 
0046 bool WinBattery::isPowerSupply() const
0047 {
0048     return m_isPowerSupply;
0049 }
0050 
0051 Solid::Battery::ChargeState WinBattery::chargeState() const
0052 {
0053     return m_state;
0054 }
0055 
0056 QSet<QString> WinBattery::getUdis()
0057 {
0058     QSet<QString> udis;
0059     HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
0060 
0061     if (INVALID_HANDLE_VALUE != hdev) {
0062         // Limit search to 100 batteries max
0063         for (int idev = 0; idev < 100; idev++) {
0064             SP_DEVICE_INTERFACE_DATA did;
0065             ZeroMemory(&did, sizeof(did));
0066             did.cbSize = sizeof(did);
0067 
0068             if (SetupDiEnumDeviceInterfaces(hdev, 0, &GUID_DEVCLASS_BATTERY, idev, &did)) {
0069                 DWORD cbRequired = 0;
0070 
0071                 SetupDiGetDeviceInterfaceDetailW(hdev, &did, 0, 0, &cbRequired, 0);
0072                 if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
0073                     char *buffer = new char[cbRequired];
0074                     SP_DEVICE_INTERFACE_DETAIL_DATA *pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA *)buffer;
0075                     ZeroMemory(pdidd, cbRequired);
0076                     pdidd->cbSize = sizeof(*pdidd);
0077                     if (SetupDiGetDeviceInterfaceDetail(hdev, &did, pdidd, cbRequired, &cbRequired, 0)) {
0078                         QString path = QString::fromWCharArray(pdidd->DevicePath);
0079                         ulong tag = WinDeviceManager::getDeviceInfo<ulong>(path, IOCTL_BATTERY_QUERY_TAG);
0080                         QString udi = QLatin1String("/org/kde/solid/win/power.battery/battery#") + QString::number(tag);
0081                         udis << udi;
0082                         m_udiToGDI[udi] = Battery(path, tag);
0083                     }
0084                     delete[] buffer;
0085                 }
0086             }
0087         }
0088 
0089         SetupDiDestroyDeviceInfoList(hdev);
0090     }
0091     return udis;
0092 }
0093 
0094 const WinBattery::Battery WinBattery::batteryInfoFromUdi(const QString &udi)
0095 {
0096     return m_udiToGDI[udi];
0097 }
0098 
0099 void WinBattery::powerChanged()
0100 {
0101     const int old_charge = m_charge;
0102     const int old_capacity = m_capacity;
0103     const Solid::Battery::ChargeState old_state = m_state;
0104     const bool old_isPowerSupply = m_isPowerSupply;
0105     const double old_energy = m_energy;
0106     const double old_energyFull = m_energyFull;
0107     const double old_energyFullDesign = m_energyFullDesign;
0108     const double old_energyRate = m_energyRate;
0109     const double old_voltage = m_voltage;
0110 
0111     BATTERY_WAIT_STATUS batteryStatusQuery;
0112     ZeroMemory(&batteryStatusQuery, sizeof(batteryStatusQuery));
0113     Battery b = m_udiToGDI[m_device->udi()];
0114     batteryStatusQuery.BatteryTag = b.second;
0115     BATTERY_STATUS status = WinDeviceManager::getDeviceInfo<BATTERY_STATUS, BATTERY_WAIT_STATUS>(b.first, IOCTL_BATTERY_QUERY_STATUS, &batteryStatusQuery);
0116 
0117     BATTERY_QUERY_INFORMATION batteryInformationQuery;
0118     ZeroMemory(&batteryInformationQuery, sizeof(batteryInformationQuery));
0119     batteryInformationQuery.BatteryTag = b.second;
0120     batteryInformationQuery.InformationLevel = BatteryInformation;
0121     BATTERY_INFORMATION info =
0122         WinDeviceManager::getDeviceInfo<BATTERY_INFORMATION, BATTERY_QUERY_INFORMATION>(b.first, IOCTL_BATTERY_QUERY_INFORMATION, &batteryInformationQuery);
0123 
0124     initSerial(b);
0125     updateBatteryTemp(b);
0126     updateTimeToEmpty(b);
0127 
0128     m_isPowerSupply = !(status.PowerState & BATTERY_POWER_ON_LINE);
0129 
0130     QString tech = QString::fromUtf8((const char *)info.Chemistry, 4);
0131 
0132     if (tech == "LION" || tech == "LI-I") {
0133         m_technology = Solid::Battery::LithiumIon;
0134     } else if (tech == "PBAC") {
0135         m_technology = Solid::Battery::LeadAcid;
0136     } else if (tech == "NICD") {
0137         m_technology = Solid::Battery::NickelCadmium;
0138     } else if (tech == "NIMH") {
0139         m_technology = Solid::Battery::NickelMetalHydride;
0140     } else {
0141         m_technology = Solid::Battery::UnknownTechnology;
0142     }
0143 
0144     m_energy = status.Capacity / 1000.0; // provided in mWh
0145     m_energyFull = info.FullChargedCapacity / 1000.0; // provided in mWh
0146     m_energyFullDesign = info.DesignedCapacity / 1000.0; // provided in mWh
0147     m_energyRate = status.Rate / 1000.0; // provided in mW
0148     m_voltage = status.Voltage / 1000.0; // provided in mV
0149 
0150     if (info.FullChargedCapacity != 0) {
0151         m_charge = (float)status.Capacity / info.FullChargedCapacity * 100.0;
0152     }
0153 
0154     if (info.DesignedCapacity != 0) {
0155         m_capacity = (float)info.FullChargedCapacity / info.DesignedCapacity * 100.0;
0156     }
0157 
0158     if (status.PowerState == 0) {
0159         m_state = Solid::Battery::NoCharge;
0160     } else if (status.PowerState & BATTERY_CHARGING) {
0161         m_state = Solid::Battery::Charging;
0162     } else if (status.PowerState & BATTERY_DISCHARGING) {
0163         m_state = Solid::Battery::Discharging;
0164     }
0165     //    else if(info.PowerState & 0x00000008)//critical
0166     //    {
0167 
0168     //    }
0169 
0170     if (info.Capabilities & BATTERY_SYSTEM_BATTERY) {
0171         m_type = Solid::Battery::PrimaryBattery;
0172     } else {
0173         m_type = Solid::Battery::UnknownBattery;
0174     }
0175 
0176     m_rechargeable = info.Technology == 1;
0177 
0178     if (m_charge != old_charge) {
0179         Q_EMIT chargePercentChanged(m_charge, m_device->udi());
0180     }
0181 
0182     if (m_capacity != old_capacity) {
0183         Q_EMIT capacityChanged(m_capacity, m_device->udi());
0184     }
0185 
0186     if (old_state != m_state) {
0187         Q_EMIT chargeStateChanged(m_state, m_device->udi());
0188     }
0189 
0190     if (old_isPowerSupply != m_isPowerSupply) {
0191         Q_EMIT powerSupplyStateChanged(m_isPowerSupply, m_device->udi());
0192     }
0193 
0194     if (old_energy != m_energy) {
0195         Q_EMIT energyChanged(m_energy, m_device->udi());
0196     }
0197 
0198     if (old_energyFull != m_energyFull) {
0199         Q_EMIT energyFullChanged(m_energyFull, m_device->udi());
0200     }
0201 
0202     if (old_energyFullDesign != m_energyFullDesign) {
0203         Q_EMIT energyFullDesignChanged(m_energyFullDesign, m_device->udi());
0204     }
0205 
0206     if (old_energyRate != m_energyRate) {
0207         Q_EMIT energyRateChanged(m_energyRate, m_device->udi());
0208     }
0209 
0210     if (old_voltage != m_voltage) {
0211         Q_EMIT voltageChanged(m_voltage, m_device->udi());
0212     }
0213 }
0214 
0215 void WinBattery::initSerial(const Battery &b)
0216 {
0217     wchar_t buffer[1024];
0218     BATTERY_QUERY_INFORMATION batteryInformationQuery;
0219     ZeroMemory(&batteryInformationQuery, sizeof(batteryInformationQuery));
0220     batteryInformationQuery.BatteryTag = b.second;
0221     batteryInformationQuery.InformationLevel = BatterySerialNumber;
0222     WinDeviceManager::getDeviceInfo<wchar_t, BATTERY_QUERY_INFORMATION>(b.first, IOCTL_BATTERY_QUERY_INFORMATION, buffer, 1024, &batteryInformationQuery);
0223 
0224     m_serial = QString::fromWCharArray(buffer);
0225 }
0226 
0227 void WinBattery::updateTimeToEmpty(const WinBattery::Battery &b)
0228 {
0229     BATTERY_QUERY_INFORMATION batteryInformationQuery;
0230     ZeroMemory(&batteryInformationQuery, sizeof(batteryInformationQuery));
0231     batteryInformationQuery.BatteryTag = b.second;
0232     batteryInformationQuery.InformationLevel = BatteryEstimatedTime;
0233     ulong time = WinDeviceManager::getDeviceInfo<ulong, BATTERY_QUERY_INFORMATION>(b.first, IOCTL_BATTERY_QUERY_INFORMATION, &batteryInformationQuery);
0234 
0235     if (time == BATTERY_UNKNOWN_TIME) {
0236         time = 0;
0237     }
0238 
0239     if (time != m_timeUntilEmpty) {
0240         m_timeUntilEmpty = time;
0241         Q_EMIT timeToEmptyChanged(time, m_device->udi());
0242     }
0243 }
0244 
0245 void WinBattery::updateBatteryTemp(const WinBattery::Battery &b)
0246 {
0247     BATTERY_QUERY_INFORMATION batteryInformationQuery;
0248     ZeroMemory(&batteryInformationQuery, sizeof(batteryInformationQuery));
0249     batteryInformationQuery.BatteryTag = b.second;
0250     batteryInformationQuery.InformationLevel = BatteryTemperature;
0251     ulong batteryTemp = WinDeviceManager::getDeviceInfo<ulong, BATTERY_QUERY_INFORMATION>(b.first, IOCTL_BATTERY_QUERY_INFORMATION, &batteryInformationQuery);
0252 
0253     if (batteryTemp != m_temperature) {
0254         m_temperature = batteryTemp;
0255         Q_EMIT temperatureChanged(batteryTemp, m_device->udi());
0256     }
0257 }
0258 
0259 Solid::Battery::Technology WinBattery::technology() const
0260 {
0261     return m_technology;
0262 }
0263 
0264 double WinBattery::energy() const
0265 {
0266     return m_energy;
0267 }
0268 
0269 double WinBattery::energyFull() const
0270 {
0271     return m_energyFull;
0272 }
0273 
0274 double WinBattery::energyFullDesign() const
0275 {
0276     return m_energyFullDesign;
0277 }
0278 
0279 double WinBattery::energyRate() const
0280 {
0281     return m_energyRate;
0282 }
0283 
0284 double WinBattery::voltage() const
0285 {
0286     return m_voltage;
0287 }
0288 
0289 bool WinBattery::isPresent() const
0290 {
0291     return true;
0292 }
0293 
0294 qlonglong WinBattery::timeToEmpty() const
0295 {
0296     return m_timeUntilEmpty;
0297 }
0298 
0299 qlonglong WinBattery::timeToFull() const
0300 {
0301     return 0;
0302 }
0303 
0304 double WinBattery::temperature() const
0305 {
0306     return m_temperature;
0307 }
0308 
0309 qlonglong WinBattery::remainingTime() const
0310 {
0311     return m_timeUntilEmpty; // FIXME
0312 }
0313 
0314 QString WinBattery::serial() const
0315 {
0316     return m_serial;
0317 }
0318 
0319 #include "moc_winbattery.cpp"