Bluetooth (Windows): detect battery level

This commit is contained in:
李通洲
2024-11-09 20:21:46 +08:00
parent acb38fa3ee
commit 07f2a71b3c
4 changed files with 113 additions and 21 deletions
+1 -1
View File
@@ -1147,7 +1147,7 @@ if(LINUX)
elseif(ANDROID)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE _FILE_OFFSET_BITS=64 "$<$<CONFIG:DEBUG>:__BIONIC_FORTIFY>" "$<$<CONFIG:DEBUG>:__BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED>")
elseif(WIN32)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN=1 _WIN32_WINNT=0x0A00 NOMINMAX) # "$<$<CONFIG:Release>:_FORTIFY_SOURCE=3>"
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 NOMINMAX UNICODE) # "$<$<CONFIG:Release>:_FORTIFY_SOURCE=3>"
elseif(APPLE)
target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE __STDC_WANT_LIB_EXT1__ _FILE_OFFSET_BITS=64 _DARWIN_C_SOURCE)
elseif(OpenBSD)
@@ -121,5 +121,8 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */)
ffBluetoothFindDeviceClose(hFind);
const char* ffBluetoothDetectBattery(FFlist* result);
ffBluetoothDetectBattery(devices);
return NULL;
}
+103 -12
View File
@@ -5,27 +5,118 @@ extern "C"
#include "util/windows/wmi.hpp"
#include "util/windows/unicode.hpp"
#include <propvarutil.h>
STDAPI InitVariantFromStringArray(_In_reads_(cElems) PCWSTR *prgsz, _In_ ULONG cElems, _Out_ VARIANT *pvar);
static const char* detectWithWmi(FFlist* result)
template <typename Fn>
struct on_scope_exit {
on_scope_exit(Fn &&fn): _fn(std::move(fn)) {}
~on_scope_exit() { this->_fn(); }
private:
Fn _fn;
};
extern "C"
const char* ffBluetoothDetectBattery(FFlist* devices)
{
FFWmiQuery query(L"SELECT __PATH FROM Win32_PnPEntity WHERE Service = 'BthHFEnum'", nullptr, FFWmiNamespace::CIMV2);
if(!query)
return "Query WMI service failed";
while(FFWmiRecord record = query.next())
IWbemClassObject* pInParams = nullptr;
on_scope_exit releaseInParams([&] { pInParams && pInParams->Release(); });
{
IWbemClassObject* pInParams = nullptr;
PCWSTR props[] = { L"{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2" };
IWbemClassObject* pnpEntityClass = nullptr;
if (FAILED(query.pService->GetObjectW(bstr_t(L"Win32_PnPEntity"), 0, nullptr, &pnpEntityClass, nullptr)))
return "Failed to get PnP entity class";
on_scope_exit releasePnpEntityClass([&] { pnpEntityClass && pnpEntityClass->Release(); });
if (FAILED(pnpEntityClass->GetMethod(bstr_t(L"GetDeviceProperties"), 0, &pInParams, NULL)))
return "Failed to get GetDeviceProperties method";
VARIANT devicePropertyKeys;
InitVariantFromStringArray(props, ARRAY_SIZE(props), &devicePropertyKeys);
record.obj->GetMethod(bstr_t(L"GetDeviceProperties"), 0, &pInParams, NULL);
pInParams->Put(L"devicePropertyKeys", 0, &devicePropertyKeys, CIM_FLAG_ARRAY | CIM_STRING);
IWbemCallResult* pResult = nullptr;
query.pService->ExecMethod(bstr_t(record.get(L"__PATH").bstrVal), bstr_t(L"GetDeviceProperties"), 0, nullptr, pInParams, nullptr, &pResult);
// TODO: parse result
PCWSTR props[] = { L"{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2", L"DEVPKEY_Bluetooth_DeviceAddress" };
if (FAILED(InitVariantFromStringArray(props, ARRAY_SIZE(props), &devicePropertyKeys)))
return "Failed to init variant from string array";
on_scope_exit releaseDevicePropertyKeys([&] { VariantClear(&devicePropertyKeys); });
if (FAILED(pInParams->Put(L"devicePropertyKeys", 0, &devicePropertyKeys, CIM_FLAG_ARRAY | CIM_STRING)))
return "Failed to put devicePropertyKeys";
}
while (FFWmiRecord record = query.next())
{
IWbemCallResult* pCallResult = nullptr;
if (FAILED(query.pService->ExecMethod(record.get(L"__PATH").bstrVal, bstr_t(L"GetDeviceProperties"), 0, nullptr, pInParams, nullptr, &pCallResult)))
continue;
on_scope_exit releaseCallResult([&] { pCallResult && pCallResult->Release(); });
IWbemClassObject* pResultObject = nullptr;
if (FAILED(pCallResult->GetResultObject(WBEM_INFINITE, &pResultObject)))
continue;
on_scope_exit releaseResultObject([&] { pResultObject && pResultObject->Release(); });
VARIANT propArray;
if (FAILED(pResultObject->Get(L"deviceProperties", 0, &propArray, nullptr, nullptr)))
continue;
on_scope_exit releasePropArray([&] { VariantClear(&propArray); });
if (propArray.vt != (VT_ARRAY | VT_UNKNOWN) ||
(propArray.parray->fFeatures & FADF_UNKNOWN) == 0 ||
propArray.parray->cDims != 1 ||
propArray.parray->rgsabound[0].cElements != 2
)
continue;
uint8_t batt = 0;
for (LONG i = 0; i < 2; i++)
{
IWbemClassObject* object = nullptr;
if (FAILED(SafeArrayGetElement(propArray.parray, &i, &object)))
continue;
FFWmiRecord rec(object);
auto data = rec.get(L"Data");
if (data.vt == VT_EMPTY)
break;
if (i == 0)
batt = data.get<uint8_t>();
else
{
FF_STRBUF_AUTO_DESTROY addr; // MAC address without colon
ffStrbufInitWSV(&addr, data.get<std::wstring_view>());
if (__builtin_expect(addr.length != 12, 0))
continue;
FF_LIST_FOR_EACH(FFBluetoothResult, bt, *devices)
{
if (bt->address.length != 12 + 5)
continue;
if (addr.chars[0] == bt->address.chars[0] &&
addr.chars[1] == bt->address.chars[1] &&
addr.chars[2] == bt->address.chars[3] &&
addr.chars[3] == bt->address.chars[4] &&
addr.chars[4] == bt->address.chars[6] &&
addr.chars[5] == bt->address.chars[7] &&
addr.chars[6] == bt->address.chars[9] &&
addr.chars[7] == bt->address.chars[10] &&
addr.chars[8] == bt->address.chars[12] &&
addr.chars[9] == bt->address.chars[13] &&
addr.chars[10] == bt->address.chars[15] &&
addr.chars[11] == bt->address.chars[16])
{
bt->battery = batt;
break;
}
}
}
}
}
return NULL;
}
+6 -8
View File
@@ -22,13 +22,7 @@ struct FFWmiRecord
{
IWbemClassObject* obj = nullptr;
explicit FFWmiRecord(IEnumWbemClassObject* pEnumerator) {
if(!pEnumerator) return;
ULONG ret;
bool ok = SUCCEEDED(pEnumerator->Next(instance.config.general.wmiTimeout, 1, &obj, &ret)) && ret;
if(!ok) obj = nullptr;
}
explicit FFWmiRecord(IWbemClassObject* obj): obj(obj) {};
FFWmiRecord(const FFWmiRecord&) = delete;
FFWmiRecord(FFWmiRecord&& other) { *this = (FFWmiRecord&&)other; }
~FFWmiRecord() { if(obj) obj->Release(); }
@@ -71,7 +65,11 @@ struct FFWmiQuery
}
FFWmiRecord next() {
FFWmiRecord result(pEnumerator);
IWbemClassObject* obj = nullptr;
ULONG ret;
pEnumerator->Next(instance.config.general.wmiTimeout, 1, &obj, &ret);
FFWmiRecord result(obj);
return result;
}
};