mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
257 lines
8.1 KiB
C
257 lines
8.1 KiB
C
#include "bluetooth.h"
|
|
#include "common/strutil.h"
|
|
|
|
#ifdef FF_HAVE_DBUS
|
|
#include "common/dbus.h"
|
|
#include "common/io.h"
|
|
|
|
/* Example dbus reply, striped to only the relevant parts:
|
|
array [ //root
|
|
dict entry( //object
|
|
object path "/org/bluez/hci0/dev_03_21_8B_91_16_4D"
|
|
array [
|
|
dict entry( //property
|
|
string "org.bluez.Device1"
|
|
array [
|
|
dict entry( //value
|
|
string "Address"
|
|
variant string "03:21:8B:91:16:4D"
|
|
)
|
|
dict entry( //value
|
|
string "Name"
|
|
variant string "JBL TUNE160BT"
|
|
)
|
|
dict entry( //value
|
|
string "Icon"
|
|
variant string "audio-headset"
|
|
)
|
|
dict entry( //value
|
|
string "Connected"
|
|
variant boolean true
|
|
)
|
|
]
|
|
)
|
|
dict entry( //property
|
|
string "org.bluez.Battery1"
|
|
array [
|
|
dict entry( //value
|
|
string "Percentage"
|
|
variant byte 100
|
|
)
|
|
]
|
|
)
|
|
]
|
|
)
|
|
]
|
|
*/
|
|
|
|
static bool detectBluetoothValue(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) {
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
|
|
return true;
|
|
}
|
|
|
|
DBusMessageIter dictIter;
|
|
dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
|
|
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) {
|
|
return true;
|
|
}
|
|
|
|
const char* deviceProperty;
|
|
dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &deviceProperty);
|
|
|
|
dbus->lib->ffdbus_message_iter_next(&dictIter);
|
|
|
|
if (ffStrEquals(deviceProperty, "Address")) {
|
|
ffDBusGetString(dbus, &dictIter, &device->address);
|
|
} else if (ffStrEquals(deviceProperty, "Name")) {
|
|
ffDBusGetString(dbus, &dictIter, &device->name);
|
|
} else if (ffStrEquals(deviceProperty, "Icon")) {
|
|
ffDBusGetString(dbus, &dictIter, &device->type);
|
|
} else if (ffStrEquals(deviceProperty, "Percentage")) {
|
|
uint64_t percentage;
|
|
if (ffDBusGetUint(dbus, &dictIter, &percentage)) {
|
|
device->battery = (uint8_t) percentage;
|
|
}
|
|
} else if (ffStrEquals(deviceProperty, "Connected")) {
|
|
ffDBusGetBool(dbus, &dictIter, &device->connected);
|
|
} else if (ffStrEquals(deviceProperty, "Paired")) {
|
|
bool paired = true;
|
|
ffDBusGetBool(dbus, &dictIter, &paired);
|
|
if (!paired) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void detectBluetoothProperty(FFDBusData* dbus, DBusMessageIter* iter, FFBluetoothResult* device) {
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
|
|
return;
|
|
}
|
|
|
|
DBusMessageIter dictIter;
|
|
dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
|
|
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) {
|
|
return;
|
|
}
|
|
|
|
const char* propertyType;
|
|
dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &propertyType);
|
|
|
|
if (!ffStrContains(propertyType, ".Device") && !ffStrContains(propertyType, ".Battery")) {
|
|
return; // We don't care about other properties
|
|
}
|
|
|
|
dbus->lib->ffdbus_message_iter_next(&dictIter);
|
|
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_ARRAY) {
|
|
return;
|
|
}
|
|
|
|
DBusMessageIter arrayIter;
|
|
dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter);
|
|
|
|
do {
|
|
bool shouldContinue = detectBluetoothValue(dbus, &arrayIter, device);
|
|
if (!shouldContinue) {
|
|
ffStrbufClear(&device->name);
|
|
break;
|
|
}
|
|
} while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
|
|
}
|
|
|
|
static FFBluetoothResult* detectBluetoothObject(FFlist* devices, FFDBusData* dbus, DBusMessageIter* iter) {
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
|
|
return NULL;
|
|
}
|
|
|
|
DBusMessageIter dictIter;
|
|
dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
|
|
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_OBJECT_PATH) {
|
|
return NULL;
|
|
}
|
|
|
|
const char* objectPath;
|
|
dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &objectPath);
|
|
|
|
// We don't want adapter objects
|
|
if (!ffStrContains(objectPath, "/dev_")) {
|
|
return NULL;
|
|
}
|
|
|
|
dbus->lib->ffdbus_message_iter_next(&dictIter);
|
|
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_ARRAY) {
|
|
return NULL;
|
|
}
|
|
|
|
DBusMessageIter arrayIter;
|
|
dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter);
|
|
|
|
FFBluetoothResult* device = FF_LIST_ADD(FFBluetoothResult, *devices);
|
|
ffStrbufInit(&device->name);
|
|
ffStrbufInit(&device->address);
|
|
ffStrbufInit(&device->type);
|
|
device->battery = 0;
|
|
device->connected = false;
|
|
|
|
do {
|
|
detectBluetoothProperty(dbus, &arrayIter, device);
|
|
} while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
|
|
|
|
return device;
|
|
}
|
|
|
|
static void detectBluetoothRoot(FFBluetoothOptions* options, FFlist* devices, FFDBusData* dbus, DBusMessageIter* iter, int32_t connectedCount) {
|
|
if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
|
|
return;
|
|
}
|
|
|
|
DBusMessageIter arrayIter;
|
|
dbus->lib->ffdbus_message_iter_recurse(iter, &arrayIter);
|
|
|
|
do {
|
|
FFBluetoothResult* device = detectBluetoothObject(devices, dbus, &arrayIter);
|
|
|
|
if (device) {
|
|
if (!options->showDisconnected && !device->connected) {
|
|
ffStrbufDestroy(&device->name);
|
|
ffStrbufDestroy(&device->address);
|
|
ffStrbufDestroy(&device->type);
|
|
--devices->length;
|
|
}
|
|
|
|
if (device->name.length == 0) {
|
|
ffStrbufSetStatic(&device->name, "Unknown Device");
|
|
}
|
|
|
|
if (device->connected && --connectedCount == 0) {
|
|
break;
|
|
}
|
|
}
|
|
} while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
|
|
}
|
|
|
|
static const char* detectBluetooth(FFBluetoothOptions* options, FFlist* devices, int32_t connectedCount) {
|
|
FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
|
|
const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus);
|
|
if (error) {
|
|
return error;
|
|
}
|
|
|
|
DBusMessage* managedObjects = ffDBusGetMethodReply(&dbus, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", NULL, NULL);
|
|
if (!managedObjects) {
|
|
return "Failed to call GetManagedObjects";
|
|
}
|
|
|
|
DBusMessageIter rootIter;
|
|
if (!dbus.lib->ffdbus_message_iter_init(managedObjects, &rootIter)) {
|
|
dbus.lib->ffdbus_message_unref(managedObjects);
|
|
return "Failed to get root iterator of GetManagedObjects";
|
|
}
|
|
|
|
detectBluetoothRoot(options, devices, &dbus, &rootIter, connectedCount);
|
|
|
|
dbus.lib->ffdbus_message_unref(managedObjects);
|
|
return NULL;
|
|
}
|
|
|
|
static uint32_t connectedDevices(void) {
|
|
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/bluetooth");
|
|
if (dirp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
uint32_t result = 0;
|
|
struct dirent* entry;
|
|
while ((entry = readdir(dirp)) != NULL) {
|
|
if (strchr(entry->d_name, ':') != NULL) {
|
|
++result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif
|
|
|
|
const char* ffDetectBluetooth(FFBluetoothOptions* options, FFlist* devices /* FFBluetoothResult */) {
|
|
#ifdef FF_HAVE_DBUS
|
|
int32_t connectedCount = -1;
|
|
if (!options->showDisconnected) {
|
|
connectedCount = (int32_t) connectedDevices();
|
|
if (connectedCount == 0) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return detectBluetooth(options, devices, connectedCount);
|
|
#else
|
|
FF_UNUSED(options, devices);
|
|
return "Fastfetch was compiled without DBus support";
|
|
#endif
|
|
}
|