Skip to content

Commit f5b987d

Browse files
committed
Battery (Windows): detect cycle count
1 parent f8eabdd commit f5b987d

File tree

7 files changed

+142
-127
lines changed

7 files changed

+142
-127
lines changed

src/detection/battery/battery.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ typedef struct FFBatteryResult
1515
double capacity;
1616
FFstrbuf status;
1717
double temperature;
18-
int32_t cycleCount;
18+
uint32_t cycleCount;
1919
} FFBatteryResult;
2020

2121
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results);

src/detection/battery/battery_android.c

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ static const char* parseTermuxApi(FFBatteryOptions* options, FFlist* results)
3535

3636
FFBatteryResult* battery = ffListAdd(results);
3737
battery->temperature = FF_BATTERY_TEMP_UNSET;
38+
battery->cycleCount = 0;
3839
ffStrbufInit(&battery->manufacturer);
3940
ffStrbufInit(&battery->modelName);
4041
ffStrbufInit(&battery->status);

src/detection/battery/battery_apple.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results)
5656
ffStrbufAppendS(&battery->modelName, "Built-in");
5757
}
5858

59-
ffCfDictGetInt(properties, CFSTR("CycleCount"), &battery->cycleCount);
59+
ffCfDictGetInt(properties, CFSTR("CycleCount"), (int32_t*) &battery->cycleCount);
6060

6161
if (!ffCfDictGetBool(properties, CFSTR("ExternalConnected"), &boolValue) && boolValue)
6262
ffStrbufAppendS(&battery->status, "AC connected, ");

src/detection/battery/battery_bsd.c

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* r
3434

3535
FFBatteryResult* battery = ffListAdd(results);
3636
battery->temperature = FF_BATTERY_TEMP_UNSET;
37+
battery->cycleCount = 0;
3738
ffStrbufInit(&battery->manufacturer);
3839
ffStrbufInit(&battery->modelName);
3940
ffStrbufInit(&battery->status);

src/detection/battery/battery_linux.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static void parseBattery(FFstrbuf* dir, const char* id, FFBatteryOptions* option
6969
ffReadFileBuffer(dir->chars, &testBatteryBuffer);
7070
ffStrbufSubstrBefore(dir, dirLength);
7171
if(dir->length)
72-
result->cycleCount = (int32_t) ffStrbufToUInt(&testBatteryBuffer, 0);
72+
result->cycleCount = (uint32_t) ffStrbufToUInt(&testBatteryBuffer, 0);
7373

7474
result->temperature = FF_BATTERY_TEMP_UNSET;
7575
if (options->temp)

src/detection/battery/battery_windows.c

+131-122
Original file line numberDiff line numberDiff line change
@@ -30,137 +30,146 @@ static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev)
3030
SetupDiDestroyDeviceInfoList(*hdev);
3131
}
3232

33-
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results)
33+
static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results)
3434
{
35-
if(options->useSetupApi)
35+
//https://learn.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
36+
HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) =
37+
SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
38+
if(hdev == INVALID_HANDLE_VALUE)
39+
return "SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY) failed";
40+
41+
SP_DEVICE_INTERFACE_DATA did = { .cbSize = sizeof(did) };
42+
for(DWORD idev = 0; SetupDiEnumDeviceInterfaces(hdev, NULL, &GUID_DEVCLASS_BATTERY, idev, &did); idev++)
3643
{
37-
//https://learn.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices
38-
HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) =
39-
SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
40-
if(hdev == INVALID_HANDLE_VALUE)
41-
return "SetupDiGetClassDevsW(&GUID_DEVCLASS_BATTERY) failed";
42-
43-
SP_DEVICE_INTERFACE_DATA did = { .cbSize = sizeof(did) };
44-
for(DWORD idev = 0; SetupDiEnumDeviceInterfaces(hdev, NULL, &GUID_DEVCLASS_BATTERY, idev, &did); idev++)
44+
DWORD cbRequired = 0;
45+
SetupDiGetDeviceInterfaceDetailW(hdev, &did, NULL, 0, &cbRequired, NULL); //Fail with not enough buffer
46+
SP_DEVICE_INTERFACE_DETAIL_DATA_W* FF_AUTO_FREE pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(cbRequired);
47+
if(!pdidd)
48+
break; //Out of memory
49+
50+
pdidd->cbSize = sizeof(*pdidd);
51+
if(!SetupDiGetDeviceInterfaceDetailW(hdev, &did, pdidd, cbRequired, &cbRequired, NULL))
52+
continue;
53+
54+
HANDLE __attribute__((__cleanup__(wrapCloseHandle))) hBattery =
55+
CreateFileW(pdidd->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
56+
57+
if(hBattery == INVALID_HANDLE_VALUE)
58+
continue;
59+
60+
BATTERY_QUERY_INFORMATION bqi = { .InformationLevel = BatteryInformation };
61+
62+
DWORD dwWait = 0;
63+
DWORD dwOut;
64+
65+
if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG, &dwWait, sizeof(dwWait), &bqi.BatteryTag, sizeof(bqi.BatteryTag), &dwOut, NULL) && bqi.BatteryTag)
66+
continue;
67+
68+
BATTERY_INFORMATION bi = {0};
69+
if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi, sizeof(bi), &dwOut, NULL))
70+
continue;
71+
72+
if(!(bi.Capabilities & BATTERY_SYSTEM_BATTERY))
73+
continue;
74+
75+
FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results);
76+
77+
if(memcmp(bi.Chemistry, "PbAc", 4) == 0)
78+
ffStrbufInitS(&battery->technology, "Lead Acid");
79+
else if(memcmp(bi.Chemistry, "LION", 4) == 0 || memcmp(bi.Chemistry, "Li-I", 4) == 0)
80+
ffStrbufInitS(&battery->technology, "Lithium Ion");
81+
else if(memcmp(bi.Chemistry, "NiCd", 4) == 0)
82+
ffStrbufInitS(&battery->technology, "Nickel Cadmium");
83+
else if(memcmp(bi.Chemistry, "NiMH", 4) == 0)
84+
ffStrbufInitS(&battery->technology, "Nickel Metal Hydride");
85+
else if(memcmp(bi.Chemistry, "NiZn", 4) == 0)
86+
ffStrbufInitS(&battery->technology, "Nickel Zinc");
87+
else if(memcmp(bi.Chemistry, "RAM\0", 4) == 0)
88+
ffStrbufInitS(&battery->technology, "Rechargeable Alkaline-Manganese");
89+
else
90+
ffStrbufInitS(&battery->technology, "Unknown");
91+
92+
{
93+
ffStrbufInit(&battery->modelName);
94+
bqi.InformationLevel = BatteryDeviceName;
95+
wchar_t name[64];
96+
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL))
97+
ffStrbufSetWS(&battery->modelName, name);
98+
}
99+
100+
{
101+
ffStrbufInit(&battery->manufacturer);
102+
bqi.InformationLevel = BatteryManufactureName;
103+
wchar_t name[64];
104+
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL))
105+
ffStrbufSetWS(&battery->manufacturer, name);
106+
}
107+
108+
battery->cycleCount = bi.CycleCount;
109+
110+
battery->temperature = 0.0/0.0;
111+
if(options->temp)
112+
{
113+
bqi.InformationLevel = BatteryTemperature;
114+
ULONG temp;
115+
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &temp, sizeof(temp), &dwOut, NULL))
116+
battery->temperature = temp;
117+
}
118+
45119
{
46-
DWORD cbRequired = 0;
47-
SetupDiGetDeviceInterfaceDetailW(hdev, &did, NULL, 0, &cbRequired, NULL); //Fail with not enough buffer
48-
SP_DEVICE_INTERFACE_DETAIL_DATA_W* FF_AUTO_FREE pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(cbRequired);
49-
if(!pdidd)
50-
break; //Out of memory
51-
52-
pdidd->cbSize = sizeof(*pdidd);
53-
if(!SetupDiGetDeviceInterfaceDetailW(hdev, &did, pdidd, cbRequired, &cbRequired, NULL))
54-
continue;
55-
56-
HANDLE __attribute__((__cleanup__(wrapCloseHandle))) hBattery =
57-
CreateFileW(pdidd->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
58-
59-
if(hBattery == INVALID_HANDLE_VALUE)
60-
continue;
61-
62-
BATTERY_QUERY_INFORMATION bqi = { .InformationLevel = BatteryInformation };
63-
64-
DWORD dwWait = 0;
65-
DWORD dwOut;
66-
67-
if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG, &dwWait, sizeof(dwWait), &bqi.BatteryTag, sizeof(bqi.BatteryTag), &dwOut, NULL) && bqi.BatteryTag)
68-
continue;
69-
70-
BATTERY_INFORMATION bi = {0};
71-
if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi, sizeof(bi), &dwOut, NULL))
72-
continue;
73-
74-
if(!(bi.Capabilities & BATTERY_SYSTEM_BATTERY))
75-
continue;
76-
77-
FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results);
78-
79-
if(memcmp(bi.Chemistry, "PbAc", 4) == 0)
80-
ffStrbufInitS(&battery->technology, "Lead Acid");
81-
else if(memcmp(bi.Chemistry, "LION", 4) == 0 || memcmp(bi.Chemistry, "Li-I", 4) == 0)
82-
ffStrbufInitS(&battery->technology, "Lithium Ion");
83-
else if(memcmp(bi.Chemistry, "NiCd", 4) == 0)
84-
ffStrbufInitS(&battery->technology, "Nickel Cadmium");
85-
else if(memcmp(bi.Chemistry, "NiMH", 4) == 0)
86-
ffStrbufInitS(&battery->technology, "Nickel Metal Hydride");
87-
else if(memcmp(bi.Chemistry, "NiZn", 4) == 0)
88-
ffStrbufInitS(&battery->technology, "Nickel Zinc");
89-
else if(memcmp(bi.Chemistry, "RAM\0", 4) == 0)
90-
ffStrbufInitS(&battery->technology, "Rechargeable Alkaline-Manganese");
120+
BATTERY_STATUS bs;
121+
BATTERY_WAIT_STATUS bws = { .BatteryTag = bqi.BatteryTag };
122+
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY)
123+
battery->capacity = bs.Capacity * 100.0 / bi.FullChargedCapacity;
91124
else
92-
ffStrbufInitS(&battery->technology, "Unknown");
93-
94-
{
95-
ffStrbufInit(&battery->modelName);
96-
bqi.InformationLevel = BatteryDeviceName;
97-
wchar_t name[64];
98-
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL))
99-
ffStrbufSetWS(&battery->modelName, name);
100-
}
101-
102-
{
103-
ffStrbufInit(&battery->manufacturer);
104-
bqi.InformationLevel = BatteryManufactureName;
105-
wchar_t name[64];
106-
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL))
107-
ffStrbufSetWS(&battery->manufacturer, name);
108-
}
109-
110-
battery->temperature = 0.0/0.0;
111-
if(options->temp)
112-
{
113-
bqi.InformationLevel = BatteryTemperature;
114-
ULONG temp;
115-
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &temp, sizeof(temp), &dwOut, NULL))
116-
battery->temperature = temp;
117-
}
118-
119-
{
120-
BATTERY_STATUS bs;
121-
BATTERY_WAIT_STATUS bws = { .BatteryTag = bqi.BatteryTag };
122-
if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY)
123-
battery->capacity = bs.Capacity * 100.0 / bi.FullChargedCapacity;
124-
else
125-
battery->capacity = 0;
126-
127-
ffStrbufInit(&battery->status);
128-
if(bs.PowerState & BATTERY_POWER_ON_LINE)
129-
ffStrbufAppendS(&battery->status, "AC Connected, ");
130-
if(bs.PowerState & BATTERY_DISCHARGING)
131-
ffStrbufAppendS(&battery->status, "Discharging, ");
132-
if(bs.PowerState & BATTERY_CHARGING)
133-
ffStrbufAppendS(&battery->status, "Charging");
134-
if(bs.PowerState & BATTERY_CRITICAL)
135-
ffStrbufAppendS(&battery->status, "Critical, ");
136-
ffStrbufTrimRight(&battery->status, ' ');
137-
ffStrbufTrimRight(&battery->status, ',');
138-
}
125+
battery->capacity = 0;
126+
127+
ffStrbufInit(&battery->status);
128+
if(bs.PowerState & BATTERY_POWER_ON_LINE)
129+
ffStrbufAppendS(&battery->status, "AC Connected, ");
130+
if(bs.PowerState & BATTERY_DISCHARGING)
131+
ffStrbufAppendS(&battery->status, "Discharging, ");
132+
if(bs.PowerState & BATTERY_CHARGING)
133+
ffStrbufAppendS(&battery->status, "Charging, ");
134+
if(bs.PowerState & BATTERY_CRITICAL)
135+
ffStrbufAppendS(&battery->status, "Critical, ");
136+
ffStrbufTrimRight(&battery->status, ' ');
137+
ffStrbufTrimRight(&battery->status, ',');
139138
}
140139
}
141-
else
140+
return NULL;
141+
}
142+
143+
static const char* detectWithNtApi(FFBatteryOptions* options, FFlist* results)
144+
{
145+
SYSTEM_BATTERY_STATE info;
146+
if (NT_SUCCESS(NtPowerInformation(SystemBatteryState, NULL, 0, &info, sizeof(info))) && info.BatteryPresent)
142147
{
143-
SYSTEM_BATTERY_STATE info;
144-
if (NT_SUCCESS(NtPowerInformation(SystemBatteryState, NULL, 0, &info, sizeof(info))) && info.BatteryPresent)
148+
FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results);
149+
ffStrbufInit(&battery->modelName);
150+
ffStrbufInit(&battery->manufacturer);
151+
ffStrbufInit(&battery->technology);
152+
ffStrbufInit(&battery->status);
153+
battery->temperature = 0.0/0.0;
154+
battery->cycleCount = 0;
155+
156+
battery->capacity = info.RemainingCapacity * 100.0 / info.MaxCapacity;
157+
if(info.AcOnLine)
145158
{
146-
FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results);
147-
ffStrbufInit(&battery->modelName);
148-
ffStrbufInit(&battery->manufacturer);
149-
ffStrbufInit(&battery->technology);
150-
ffStrbufInit(&battery->status);
151-
battery->temperature = 0.0/0.0;
152-
153-
battery->capacity = info.RemainingCapacity * 100.0 / info.MaxCapacity;
154-
if(info.AcOnLine)
155-
{
156-
ffStrbufAppendS(&battery->status, "AC Connected");
157-
if(info.Charging)
158-
ffStrbufAppendS(&battery->status, ", Charging");
159-
}
160-
else if(info.Discharging)
161-
ffStrbufAppendS(&battery->status, "Discharging");
159+
ffStrbufAppendS(&battery->status, "AC Connected");
160+
if(info.Charging)
161+
ffStrbufAppendS(&battery->status, ", Charging");
162162
}
163+
else if(info.Discharging)
164+
ffStrbufAppendS(&battery->status, "Discharging");
165+
return NULL;
163166
}
167+
return "NtPowerInformation(SystemBatteryState) failed";
168+
}
164169

165-
return NULL;
170+
const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results)
171+
{
172+
return true
173+
? detectWithSetupApi(options, results)
174+
: detectWithNtApi(options, results);
166175
}

src/modules/battery/battery.c

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include "modules/battery/battery.h"
77
#include "util/stringUtils.h"
88

9-
#define FF_BATTERY_NUM_FORMAT_ARGS 5
9+
#define FF_BATTERY_NUM_FORMAT_ARGS 7
1010

1111
static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uint8_t index)
1212
{
@@ -70,6 +70,7 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin
7070
{FF_FORMAT_ARG_TYPE_STRBUF, &capacityStr},
7171
{FF_FORMAT_ARG_TYPE_STRBUF, &result->status},
7272
{FF_FORMAT_ARG_TYPE_DOUBLE, &result->temperature},
73+
{FF_FORMAT_ARG_TYPE_UINT, &result->cycleCount},
7374
});
7475
}
7576
}
@@ -215,6 +216,7 @@ void ffGenerateBatteryJsonResult(FFBatteryOptions* options, yyjson_mut_doc* doc,
215216
yyjson_mut_obj_add_strbuf(doc, obj, "status", &battery->status);
216217
yyjson_mut_obj_add_strbuf(doc, obj, "technology", &battery->technology);
217218
yyjson_mut_obj_add_real(doc, obj, "temperature", battery->temperature);
219+
yyjson_mut_obj_add_uint(doc, obj, "cycleCount", battery->cycleCount);
218220
}
219221

220222
FF_LIST_FOR_EACH(FFBatteryResult, battery, results)
@@ -233,7 +235,9 @@ void ffPrintBatteryHelpFormat(void)
233235
"Battery model",
234236
"Battery technology",
235237
"Battery capacity (percentage)",
236-
"Battery status"
238+
"Battery status",
239+
"Battery temperature",
240+
"Battery cycle count",
237241
});
238242
}
239243

0 commit comments

Comments
 (0)