mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 10:22:12 +02:00
Bluetooth (Windows): uses CM API to detect BTH battery detection
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -1038,7 +1038,6 @@ elseif(WIN32)
|
||||
src/detection/battery/battery_windows.c
|
||||
src/detection/bios/bios_windows.c
|
||||
src/detection/bluetooth/bluetooth_windows.c
|
||||
src/detection/bluetooth/bluetooth_windows.cpp
|
||||
src/detection/bluetoothradio/bluetoothradio_windows.c
|
||||
src/detection/board/board_windows.c
|
||||
src/detection/bootmgr/bootmgr_windows.c
|
||||
|
||||
@@ -1,12 +1,102 @@
|
||||
#include "bluetooth.h"
|
||||
#include "common/library.h"
|
||||
#include "common/mallocHelper.h"
|
||||
#include "common/windows/unicode.h"
|
||||
|
||||
#define INITGUID
|
||||
#include <windows.h>
|
||||
#include <bluetoothapis.h>
|
||||
#include <cfgmgr32.h>
|
||||
#include <devpkey.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wpointer-sign"
|
||||
|
||||
// https://github.com/wine-mirror/wine/blob/ab6f4584b89f28504b0b277c0b4c723a86b4d6b7/include/ddk/bthguid.h#L4
|
||||
/* DEVPROP_TYPE_STRING */
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Bluetooth_DeviceAddress, 0x2bd67d8b, 0x8beb, 0x48d5, 0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a, 1);
|
||||
/* DEVPROP_TYPE_UINT32 */
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Bluetooth_ClassOfDevice, 0x2bd67d8b, 0x8beb, 0x48d5, 0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a, 10);
|
||||
/* DEVPROP_TYPE_FILETIME */
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Bluetooth_LastConnectedTime, 0x2bd67d8b, 0x8beb, 0x48d5, 0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a, 11);
|
||||
/* DEVPROP_TYPE_GUID */
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Bluetooth_ServiceGUID, 0x2bd67d8b, 0x8beb, 0x48d5, 0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a, 2);
|
||||
/* DEVPROP_TYPE_UINT8 */
|
||||
DEFINE_DEVPROPKEY(DEVPKEY_Bluetooth_BatteryLevel, 0x104ea319, 0x6ee2, 0x4701, 0xbd, 0x47, 0x8d, 0xdb, 0xf4, 0x25, 0xbb, 0xe5, 2);
|
||||
|
||||
// TODO: use CM API to fetch bluetooth devices instead of BluetoothFindFirstDevice, if we find DEVPKEY_Bluetooth_IsConnected or similar
|
||||
#define GUID_DEVCLASS_BLUETOOTH_STRING L"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}" // Found in <devguid.h>
|
||||
#define GUID_DEVCLASS_MEDIA_STRING L"{4d36e96c-e325-11ce-bfc1-08002be10318}" // Found in <devguid.h>
|
||||
|
||||
static const char* ffBluetoothDetectBattery(FFlist* devices) {
|
||||
ULONG idListLength = 0;
|
||||
CONFIGRET status = CM_Get_Device_ID_List_SizeW(&idListLength, GUID_DEVCLASS_MEDIA_STRING, CM_GETIDLIST_FILTER_PRESENT);
|
||||
if (status != CR_SUCCESS) {
|
||||
return "CM_Get_Device_ID_List_SizeW failed";
|
||||
}
|
||||
|
||||
if (idListLength == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wchar_t* FF_AUTO_FREE idList = (wchar_t*) malloc((size_t) idListLength * sizeof(wchar_t));
|
||||
if (!idList) {
|
||||
return "malloc() failed";
|
||||
}
|
||||
|
||||
status = CM_Get_Device_ID_ListW(GUID_DEVCLASS_MEDIA_STRING, idList, idListLength, CM_GETIDLIST_FILTER_PRESENT);
|
||||
if (status != CR_SUCCESS) {
|
||||
return "CM_Get_Device_ID_ListW failed";
|
||||
}
|
||||
|
||||
for (const wchar_t* deviceId = idList; *deviceId; deviceId += wcslen(deviceId) + 1) {
|
||||
DEVINST devInst = 0;
|
||||
|
||||
// Hands-Free profile service; headsets often expose battery level through this media device node rather than the Bluetooth device node
|
||||
// BthHFEnum
|
||||
if (CM_Locate_DevNodeW(&devInst, (DEVINSTID_W) deviceId, CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t battery = 0;
|
||||
{
|
||||
DEVPROPTYPE devPropertyType = DEVPROP_TYPE_EMPTY;
|
||||
ULONG propertySize = sizeof(battery);
|
||||
if (CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Bluetooth_BatteryLevel, &devPropertyType, (PBYTE) &battery, &propertySize, 0) != CR_SUCCESS || devPropertyType != DEVPROP_TYPE_BYTE || propertySize != sizeof(battery)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
WCHAR deviceAddress[13]; // 6 bytes in hex + null terminator
|
||||
{
|
||||
DEVPROPTYPE devPropertyType = DEVPROP_TYPE_EMPTY;
|
||||
ULONG propertySize = sizeof(deviceAddress);
|
||||
if (CM_Get_DevNode_PropertyW(devInst, &DEVPKEY_Bluetooth_DeviceAddress, &devPropertyType, (PBYTE) deviceAddress, &propertySize, 0) != CR_SUCCESS || devPropertyType != DEVPROP_TYPE_STRING || propertySize != sizeof(deviceAddress)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
FF_LIST_FOR_EACH (FFBluetoothResult, bt, *devices) {
|
||||
if (deviceAddress[0] == bt->address.chars[0] &&
|
||||
deviceAddress[1] == bt->address.chars[1] &&
|
||||
deviceAddress[2] == bt->address.chars[3] &&
|
||||
deviceAddress[3] == bt->address.chars[4] &&
|
||||
deviceAddress[4] == bt->address.chars[6] &&
|
||||
deviceAddress[5] == bt->address.chars[7] &&
|
||||
deviceAddress[6] == bt->address.chars[9] &&
|
||||
deviceAddress[7] == bt->address.chars[10] &&
|
||||
deviceAddress[8] == bt->address.chars[12] &&
|
||||
deviceAddress[9] == bt->address.chars[13] &&
|
||||
deviceAddress[10] == bt->address.chars[15] &&
|
||||
deviceAddress[11] == bt->address.chars[16]) {
|
||||
bt->battery = battery;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
||||
FF_LIBRARY_LOAD_MESSAGE(bluetoothapis, "bluetoothapis.dll", 1)
|
||||
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(bluetoothapis, BluetoothFindFirstDevice)
|
||||
@@ -125,8 +215,9 @@ const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FF
|
||||
|
||||
ffBluetoothFindDeviceClose(hFind);
|
||||
|
||||
const char* ffBluetoothDetectBattery(FFlist * result);
|
||||
ffBluetoothDetectBattery(devices);
|
||||
if (devices->length > 0) {
|
||||
ffBluetoothDetectBattery(devices);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
extern "C" {
|
||||
#include "bluetooth.h"
|
||||
}
|
||||
#include "common/windows/wmi.hpp"
|
||||
#include "common/windows/unicode.hpp"
|
||||
#include "common/windows/util.hpp"
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
IWbemClassObject* pInParams = nullptr;
|
||||
on_scope_exit releaseInParams([&] { pInParams && pInParams->Release(); });
|
||||
{
|
||||
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";
|
||||
}
|
||||
|
||||
FFWmiVariant devicePropertyKeys({ L"{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2", L"DEVPKEY_Bluetooth_DeviceAddress" });
|
||||
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((LONG) 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 = ffStrbufCreateWSV(data.get<std::wstring_view>()); // MAC address without colon
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user