master ohmu/fastfetch-openbsd / src / detection / bluetoothradio / bluetoothradio_linux.c
ohmu  ·  2026-10-15
  1#include "bluetoothradio.h"
  2#include "common/debug.h"
  3#include "common/strutil.h"
  4
  5#ifdef FF_HAVE_DBUS
  6    #include "common/dbus.h"
  7    #include "common/io.h"
  8
  9/* Example dbus reply:
 10array [
 11    dict entry(
 12        string "Address"
 13        variant string "XX:XX:XX:XX:XX:XX"
 14    )
 15    dict entry(
 16        string "Name"
 17        variant string "xxxxxxxx"
 18    )
 19    dict entry(
 20        string "Powered"
 21        variant boolean true
 22    )
 23    dict entry(
 24        string "PowerState"
 25        variant string "on"
 26    )
 27    dict entry(
 28        string "Manufacturer"
 29        variant uint16 2
 30    )
 31    dict entry(
 32        string "Version"
 33        variant byte 12
 34    )
 35]
 36*/
 37
 38static const char* detectBluetoothProperty(FFBluetoothRadioResult* device, FFDBusData* dbus, DBusMessageIter* iter) {
 39    if (dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) {
 40        FF_DEBUG("Expected dict entry");
 41        return "Expected dict entry";
 42    }
 43
 44    DBusMessageIter dictIter;
 45    dbus->lib->ffdbus_message_iter_recurse(iter, &dictIter);
 46
 47    if (dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_STRING) {
 48        FF_DEBUG("Expected dict entry key to be a string");
 49        return "Expected dict entry key to be a string";
 50    }
 51
 52    const char* deviceProperty;
 53    dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &deviceProperty);
 54
 55    dbus->lib->ffdbus_message_iter_next(&dictIter);
 56
 57    if (ffStrEquals(deviceProperty, "Address")) {
 58        ffDBusGetString(dbus, &dictIter, &device->address);
 59    } else if (ffStrEquals(deviceProperty, "Alias")) {
 60        ffDBusGetString(dbus, &dictIter, &device->name);
 61    } else if (ffStrEquals(deviceProperty, "Manufacturer")) {
 62        uint64_t vendorId;
 63        if (ffDBusGetUint(dbus, &dictIter, &vendorId)) {
 64            ffStrbufSetStatic(&device->vendor, ffBluetoothRadioGetVendor((uint32_t) vendorId));
 65        }
 66    } else if (ffStrEquals(deviceProperty, "Version")) {
 67        uint64_t version;
 68        if (ffDBusGetUint(dbus, &dictIter, &version)) {
 69            device->lmpVersion = (int32_t) version;
 70        }
 71    } else if (ffStrEquals(deviceProperty, "Powered")) {
 72        ffDBusGetBool(dbus, &dictIter, &device->enabled);
 73    } else if (ffStrEquals(deviceProperty, "Discoverable")) {
 74        ffDBusGetBool(dbus, &dictIter, &device->discoverable);
 75    } else if (ffStrEquals(deviceProperty, "Pairable")) {
 76        ffDBusGetBool(dbus, &dictIter, &device->connectable);
 77    }
 78
 79    return nullptr;
 80}
 81
 82static const char* detectBluetoothRoot(FFBluetoothRadioResult* device, const char* hciName, FFDBusData* dbus) {
 83    char objPath[300];
 84    snprintf(objPath, sizeof(objPath), "/org/bluez/%s", hciName);
 85
 86    DBusMessage* properties = ffDBusGetMethodReply(dbus, "org.bluez", objPath, "org.freedesktop.DBus.Properties", "GetAll", "org.bluez.Adapter1", nullptr);
 87    if (!properties) {
 88        FF_DEBUG("Failed to call org.freedesktop.DBus.Properties.GetAll");
 89        return "Failed to call org.freedesktop.DBus.Properties.GetAll";
 90    }
 91
 92    DBusMessageIter rootIter;
 93    if (!dbus->lib->ffdbus_message_iter_init(properties, &rootIter)) {
 94        dbus->lib->ffdbus_message_unref(properties);
 95        FF_DEBUG("Failed to get root iterator of org.freedesktop.DBus.Properties.GetAll");
 96        return "Failed to get root iterator of org.freedesktop.DBus.Properties.GetAll";
 97    }
 98
 99    if (dbus->lib->ffdbus_message_iter_get_arg_type(&rootIter) != DBUS_TYPE_ARRAY) {
100        dbus->lib->ffdbus_message_unref(properties);
101        FF_DEBUG("Expected array");
102        return "Expected array";
103    }
104
105    DBusMessageIter arrayIter;
106    dbus->lib->ffdbus_message_iter_recurse(&rootIter, &arrayIter);
107
108    do {
109        detectBluetoothProperty(device, dbus, &arrayIter);
110    } while (dbus->lib->ffdbus_message_iter_next(&arrayIter));
111
112    dbus->lib->ffdbus_message_unref(properties);
113    return nullptr;
114}
115
116static const char* detectBluetooth(FFlist* devices) {
117    FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/bluetooth");
118    if (dirp == nullptr) {
119        FF_DEBUG("opendir(\"/sys/class/bluetooth\") failed: %s", strerror(errno));
120        return "Failed to open /sys/class/bluetooth";
121    }
122
123    FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
124    const char* error = ffDBusLoadData(DBUS_BUS_SYSTEM, &dbus);
125    if (error) {
126        return error;
127    }
128
129    struct dirent* entry;
130    while ((entry = readdir(dirp)) != nullptr) {
131        if (entry->d_name[0] == '.') {
132            continue;
133        }
134
135        if (strchr(entry->d_name, ':') != nullptr) { // ignore connected devices
136            continue;
137        }
138
139        FFBluetoothRadioResult* device = FF_LIST_ADD(FFBluetoothRadioResult, *devices);
140        ffStrbufInit(&device->name);
141        ffStrbufInit(&device->address);
142        ffStrbufInitStatic(&device->vendor, "Unknown");
143        device->lmpVersion = INT_MIN;
144        device->lmpSubversion = INT_MIN;
145        device->enabled = false;
146        detectBluetoothRoot(device, entry->d_name, &dbus);
147    }
148    return nullptr;
149}
150
151#endif
152
153const char* ffDetectBluetoothRadio(FFlist* devices /* FFBluetoothRadioResult */) {
154#ifdef FF_HAVE_DBUS
155    return detectBluetooth(devices);
156#else
157    FF_UNUSED(devices)
158    FF_DEBUG("Fastfetch was compiled without DBus support");
159    return "Fastfetch was compiled without DBus support";
160#endif
161}