From 07f2a71b3c50a673f64caa3572400d0f4070b09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sat, 9 Nov 2024 20:21:46 +0800 Subject: [PATCH] Bluetooth (Windows): detect battery level --- CMakeLists.txt | 2 +- src/detection/bluetooth/bluetooth_windows.c | 3 + src/detection/bluetooth/bluetooth_windows.cpp | 115 ++++++++++++++++-- src/util/windows/wmi.hpp | 14 +-- 4 files changed, 113 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82db4936e..be01e71c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1147,7 +1147,7 @@ if(LINUX) elseif(ANDROID) target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE _XOPEN_SOURCE _FILE_OFFSET_BITS=64 "$<$:__BIONIC_FORTIFY>" "$<$:__BIONIC_FORTIFY_RUNTIME_CHECKS_ENABLED>") elseif(WIN32) - target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN=1 _WIN32_WINNT=0x0A00 NOMINMAX) # "$<$:_FORTIFY_SOURCE=3>" + target_compile_definitions(libfastfetch PUBLIC _GNU_SOURCE WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 NOMINMAX UNICODE) # "$<$:_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) diff --git a/src/detection/bluetooth/bluetooth_windows.c b/src/detection/bluetooth/bluetooth_windows.c index e163214d3..80b83837a 100644 --- a/src/detection/bluetooth/bluetooth_windows.c +++ b/src/detection/bluetooth/bluetooth_windows.c @@ -121,5 +121,8 @@ const char* ffDetectBluetooth(FFlist* devices /* FFBluetoothResult */) ffBluetoothFindDeviceClose(hFind); + const char* ffBluetoothDetectBattery(FFlist* result); + ffBluetoothDetectBattery(devices); + return NULL; } diff --git a/src/detection/bluetooth/bluetooth_windows.cpp b/src/detection/bluetooth/bluetooth_windows.cpp index a0d38f8b2..70fc1de18 100644 --- a/src/detection/bluetooth/bluetooth_windows.cpp +++ b/src/detection/bluetooth/bluetooth_windows.cpp @@ -5,27 +5,118 @@ extern "C" #include "util/windows/wmi.hpp" #include "util/windows/unicode.hpp" -#include - STDAPI InitVariantFromStringArray(_In_reads_(cElems) PCWSTR *prgsz, _In_ ULONG cElems, _Out_ VARIANT *pvar); -static const char* detectWithWmi(FFlist* result) +template +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(); + else + { + FF_STRBUF_AUTO_DESTROY addr; // MAC address without colon + ffStrbufInitWSV(&addr, data.get()); + 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; } diff --git a/src/util/windows/wmi.hpp b/src/util/windows/wmi.hpp index 0c7096231..f4340ae7b 100644 --- a/src/util/windows/wmi.hpp +++ b/src/util/windows/wmi.hpp @@ -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; } };