Skip to content

Commit 07f2a71

Browse files
committed
Bluetooth (Windows): detect battery level
1 parent acb38fa commit 07f2a71

File tree

4 files changed

+113
-21
lines changed

4 files changed

+113
-21
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ if(LINUX)
11471147
elseif(ANDROID)
11481148
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE _FILE_OFFSET_BITS=64 "$<$<CONFIG:DEBUG>:__BIONIC_FORTIFY>" "$<$<CONFIG:DEBUG>:__BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED>")
11491149
elseif(WIN32)
1150-
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN=1 _WIN32_WINNT=0x0A00 NOMINMAX) # "$<$<CONFIG:Release>:_FORTIFY_SOURCE=3>"
1150+
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 NOMINMAX UNICODE) # "$<$<CONFIG:Release>:_FORTIFY_SOURCE=3>"
11511151
elseif(APPLE)
11521152
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE __STDC_WANT_LIB_EXT1__ _FILE_OFFSET_BITS=64 _DARWIN_C_SOURCE)
11531153
elseif(OpenBSD)

src/detection/bluetooth/bluetooth_windows.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,8 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */)
121121

122122
ffBluetoothFindDeviceClose(hFind);
123123

124+
const char* ffBluetoothDetectBattery(FFlist* result);
125+
ffBluetoothDetectBattery(devices);
126+
124127
return NULL;
125128
}

src/detection/bluetooth/bluetooth_windows.cpp

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,118 @@ extern "C"
55
#include "util/windows/wmi.hpp"
66
#include "util/windows/unicode.hpp"
77

8-
#include <propvarutil.h>
9-
108
STDAPI InitVariantFromStringArray(_In_reads_(cElems) PCWSTR *prgsz, _In_ ULONG cElems, _Out_ VARIANT *pvar);
119

12-
static const char* detectWithWmi(FFlist* result)
10+
template <typename Fn>
11+
struct on_scope_exit {
12+
on_scope_exit(Fn &&fn): _fn(std::move(fn)) {}
13+
~on_scope_exit() { this->_fn(); }
14+
15+
private:
16+
Fn _fn;
17+
};
18+
19+
extern "C"
20+
const char* ffBluetoothDetectBattery(FFlist* devices)
1321
{
1422
FFWmiQuery query(L"SELECT __PATH FROM Win32_PnPEntity WHERE Service = 'BthHFEnum'", nullptr, FFWmiNamespace::CIMV2);
1523
if(!query)
1624
return "Query WMI service failed";
1725

18-
while(FFWmiRecord record = query.next())
26+
IWbemClassObject* pInParams = nullptr;
27+
on_scope_exit releaseInParams([&] { pInParams && pInParams->Release(); });
1928
{
20-
IWbemClassObject* pInParams = nullptr;
21-
PCWSTR props[] = { L"{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2" };
29+
IWbemClassObject* pnpEntityClass = nullptr;
30+
31+
if (FAILED(query.pService->GetObjectW(bstr_t(L"Win32_PnPEntity"), 0, nullptr, &pnpEntityClass, nullptr)))
32+
return "Failed to get PnP entity class";
33+
on_scope_exit releasePnpEntityClass([&] { pnpEntityClass && pnpEntityClass->Release(); });
34+
35+
if (FAILED(pnpEntityClass->GetMethod(bstr_t(L"GetDeviceProperties"), 0, &pInParams, NULL)))
36+
return "Failed to get GetDeviceProperties method";
37+
2238
VARIANT devicePropertyKeys;
23-
InitVariantFromStringArray(props, ARRAY_SIZE(props), &devicePropertyKeys);
24-
record.obj->GetMethod(bstr_t(L"GetDeviceProperties"), 0, &pInParams, NULL);
25-
pInParams->Put(L"devicePropertyKeys", 0, &devicePropertyKeys, CIM_FLAG_ARRAY | CIM_STRING);
26-
IWbemCallResult* pResult = nullptr;
27-
query.pService->ExecMethod(bstr_t(record.get(L"__PATH").bstrVal), bstr_t(L"GetDeviceProperties"), 0, nullptr, pInParams, nullptr, &pResult);
28-
// TODO: parse result
39+
PCWSTR props[] = { L"{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2", L"DEVPKEY_Bluetooth_DeviceAddress" };
40+
41+
if (FAILED(InitVariantFromStringArray(props, ARRAY_SIZE(props), &devicePropertyKeys)))
42+
return "Failed to init variant from string array";
43+
on_scope_exit releaseDevicePropertyKeys([&] { VariantClear(&devicePropertyKeys); });
44+
45+
if (FAILED(pInParams->Put(L"devicePropertyKeys", 0, &devicePropertyKeys, CIM_FLAG_ARRAY | CIM_STRING)))
46+
return "Failed to put devicePropertyKeys";
47+
}
48+
49+
while (FFWmiRecord record = query.next())
50+
{
51+
IWbemCallResult* pCallResult = nullptr;
52+
53+
if (FAILED(query.pService->ExecMethod(record.get(L"__PATH").bstrVal, bstr_t(L"GetDeviceProperties"), 0, nullptr, pInParams, nullptr, &pCallResult)))
54+
continue;
55+
on_scope_exit releaseCallResult([&] { pCallResult && pCallResult->Release(); });
56+
57+
IWbemClassObject* pResultObject = nullptr;
58+
if (FAILED(pCallResult->GetResultObject(WBEM_INFINITE, &pResultObject)))
59+
continue;
60+
on_scope_exit releaseResultObject([&] { pResultObject && pResultObject->Release(); });
61+
62+
VARIANT propArray;
63+
if (FAILED(pResultObject->Get(L"deviceProperties", 0, &propArray, nullptr, nullptr)))
64+
continue;
65+
on_scope_exit releasePropArray([&] { VariantClear(&propArray); });
66+
67+
if (propArray.vt != (VT_ARRAY | VT_UNKNOWN) ||
68+
(propArray.parray->fFeatures & FADF_UNKNOWN) == 0 ||
69+
propArray.parray->cDims != 1 ||
70+
propArray.parray->rgsabound[0].cElements != 2
71+
)
72+
continue;
73+
74+
uint8_t batt = 0;
75+
for (LONG i = 0; i < 2; i++)
76+
{
77+
IWbemClassObject* object = nullptr;
78+
if (FAILED(SafeArrayGetElement(propArray.parray, &i, &object)))
79+
continue;
80+
81+
FFWmiRecord rec(object);
82+
auto data = rec.get(L"Data");
83+
if (data.vt == VT_EMPTY)
84+
break;
85+
86+
if (i == 0)
87+
batt = data.get<uint8_t>();
88+
else
89+
{
90+
FF_STRBUF_AUTO_DESTROY addr; // MAC address without colon
91+
ffStrbufInitWSV(&addr, data.get<std::wstring_view>());
92+
if (__builtin_expect(addr.length != 12, 0))
93+
continue;
94+
95+
FF_LIST_FOR_EACH(FFBluetoothResult, bt, *devices)
96+
{
97+
if (bt->address.length != 12 + 5)
98+
continue;
99+
100+
if (addr.chars[0] == bt->address.chars[0] &&
101+
addr.chars[1] == bt->address.chars[1] &&
102+
addr.chars[2] == bt->address.chars[3] &&
103+
addr.chars[3] == bt->address.chars[4] &&
104+
addr.chars[4] == bt->address.chars[6] &&
105+
addr.chars[5] == bt->address.chars[7] &&
106+
addr.chars[6] == bt->address.chars[9] &&
107+
addr.chars[7] == bt->address.chars[10] &&
108+
addr.chars[8] == bt->address.chars[12] &&
109+
addr.chars[9] == bt->address.chars[13] &&
110+
addr.chars[10] == bt->address.chars[15] &&
111+
addr.chars[11] == bt->address.chars[16])
112+
{
113+
bt->battery = batt;
114+
break;
115+
}
116+
}
117+
}
118+
}
29119
}
120+
30121
return NULL;
31122
}

src/util/windows/wmi.hpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,7 @@ struct FFWmiRecord
2222
{
2323
IWbemClassObject* obj = nullptr;
2424

25-
explicit FFWmiRecord(IEnumWbemClassObject* pEnumerator) {
26-
if(!pEnumerator) return;
27-
28-
ULONG ret;
29-
bool ok = SUCCEEDED(pEnumerator->Next(instance.config.general.wmiTimeout, 1, &obj, &ret)) && ret;
30-
if(!ok) obj = nullptr;
31-
}
25+
explicit FFWmiRecord(IWbemClassObject* obj): obj(obj) {};
3226
FFWmiRecord(const FFWmiRecord&) = delete;
3327
FFWmiRecord(FFWmiRecord&& other) { *this = (FFWmiRecord&&)other; }
3428
~FFWmiRecord() { if(obj) obj->Release(); }
@@ -71,7 +65,11 @@ struct FFWmiQuery
7165
}
7266

7367
FFWmiRecord next() {
74-
FFWmiRecord result(pEnumerator);
68+
IWbemClassObject* obj = nullptr;
69+
ULONG ret;
70+
pEnumerator->Next(instance.config.general.wmiTimeout, 1, &obj, &ret);
71+
72+
FFWmiRecord result(obj);
7573
return result;
7674
}
7775
};

0 commit comments

Comments
 (0)