Bluetooth module

This commit is contained in:
Linus Dierheimer 2023-01-24 10:39:33 +01:00
parent e0b2db17c2
commit 25e81b39f9
No known key found for this signature in database
GPG Key ID: 74FA57726CDD7B61
18 changed files with 622 additions and 214 deletions

View File

@ -1,3 +1,8 @@
# dev
Features:
* Bluetooth module
# 1.9.0
Notable Changes:

View File

@ -232,6 +232,7 @@ set(LIBFASTFETCH_SRC
src/common/printing.c
src/common/properties.c
src/common/settings.c
src/detection/bluetooth/bluetooth.c
src/detection/cpu/cpu.c
src/detection/cpuUsage/cpuUsage.c
src/detection/datetime.c
@ -254,6 +255,7 @@ set(LIBFASTFETCH_SRC
src/logo/logo.c
src/modules/battery.c
src/modules/bios.c
src/modules/bluetooth.c
src/modules/board.c
src/modules/brightness.c
src/modules/break.c
@ -308,6 +310,7 @@ set(LIBFASTFETCH_SRC
if(LINUX)
list(APPEND LIBFASTFETCH_SRC
src/common/dbus.c
src/common/networking_linux.c
src/common/processing_linux.c
src/detection/battery/battery_linux.c
@ -318,6 +321,7 @@ if(LINUX)
src/detection/cpu/cpu_linux.c
src/detection/cpuUsage/cpuUsage_linux.c
src/detection/cursor/cursor_linux.c
src/detection/bluetooth/bluetooth_linux.c
src/detection/disk/disk_linux.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/wayland.c

View File

@ -45,7 +45,7 @@ The following libraries are used if present at runtime:
* [`libmagickcore` (ImageMagick)](https://www.imagemagick.org/): Images in terminal using sixel or kitty graphics protocol.
* [`libchafa`](https://github.com/hpjansson/chafa): Image output as ascii art.
* [`libZ`](https://www.zlib.net/): Faster image output when using kitty graphics protocol.
* [`libDBus`](https://www.freedesktop.org/wiki/Software/dbus): Needed for detecting current media player and song.
* [`libDBus`](https://www.freedesktop.org/wiki/Software/dbus): Bluetooth, Player & Media detection.
* [`libEGL`](https://www.khronos.org/registry/EGL/),
[`libGLX`](https://dri.freedesktop.org/wiki/GLX/),
[`libOSMesa`](https://docs.mesa3d.org/osmesa.html): At least one of them is needed by the OpenGL module for gl context creation.
@ -88,7 +88,7 @@ All categories not listed here should work without needing a specific implementa
##### Available Modules
```
Battery, Bios, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, GPU, Host, Icons, Kernel, Locale, LocalIP, Media, Memory, OpenCL, OpenGL, Packages, Player, Power Adapter, Processes, PublicIP, Separator OS, Shell, Swap, Terminal, Terminal Font, Theme, Time, Title, Uptime, Vulkan, Wifi, WM, WMTheme
Battery, Bios, Bluetooth, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, GPU, Host, Icons, Kernel, Locale, LocalIP, Media, Memory, OpenCL, OpenGL, Packages, Player, Power Adapter, Processes, PublicIP, Separator OS, Shell, Swap, Terminal, Terminal Font, Theme, Time, Title, Uptime, Vulkan, Wifi, WM, WMTheme
```
##### Builtin logos

View File

@ -42,6 +42,7 @@ __fastfetch_complete_help()
"vulkan-format"
"opengl-format"
"opencl-format"
"bluetooth-format"
)
COMPREPLY=($(compgen -W "${__ff_helps[*]}" -- "$CURRENT_WORD"))
}
@ -349,6 +350,9 @@ __fastfetch_completion()
"--users-key"
"--users-format"
"--users-error"
"--bluetooth-key"
"--bluetooth-format"
"--bluetooth-error"
)
local FF_OPTIONS_PATH=(

View File

@ -1 +1 @@
--structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Weather:Break:Colors
--structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Bluetooth:Weather:Break:Colors

View File

@ -28,3 +28,4 @@
--vulkan-format driver: {}; Api Version: {}; Conformance Version: {}
--opengl-format version: {}; renderer: {}; vendor: {}
--opencl-format version: {}; device: {}; vendor: {}
--bluetooth-format Name: {}; Address: {}; Type: {}; Battery: {}

201
src/common/dbus.c Normal file
View File

@ -0,0 +1,201 @@
#include "dbus.h"
#include "common/thread.h"
static bool loadLibSymbols(const FFinstance* instance, FFDBusLibrary* lib)
{
FF_LIBRARY_LOAD(dbus, &instance->config.libDBus, false, "libdbus-1" FF_LIBRARY_EXTENSION, 4);
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_bus_get, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_new_method_call, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_init, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_init_append, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_append_basic, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_get_arg_type, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_get_basic, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_recurse, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_has_next, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_iter_next, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_message_unref, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_send_with_reply_and_block, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_connection_flush, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_pending_call_block, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_pending_call_steal_reply, false)
FF_LIBRARY_LOAD_SYMBOL_PTR(dbus, lib, dbus_pending_call_unref, false)
return true;
}
static const FFDBusLibrary* loadLib(const FFinstance* instance)
{
static FFDBusLibrary lib;
static bool loaded = false;
static bool loadSuccess = false;
static FFThreadMutex mutex = FF_THREAD_MUTEX_INITIALIZER;
ffThreadMutexLock(&mutex);
if(!loaded)
{
loaded = true;
loadSuccess = loadLibSymbols(instance, &lib);
}
ffThreadMutexUnlock(&mutex);
return loadSuccess ? &lib : NULL;
}
const char* ffDBusLoadData(const FFinstance* instance, DBusBusType busType, FFDBusData* data)
{
data->lib = loadLib(instance);
if(data->lib == NULL)
return "Failed to load DBus library";
data->connection = data->lib->ffdbus_bus_get(busType, NULL);
if(data->connection == NULL)
return "Failed to connect to DBus";
return NULL;
}
bool ffDBusGetValue(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result)
{
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
if(argType == DBUS_TYPE_STRING)
{
const char* value = NULL;
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
if(!ffStrSet(value))
return false;
ffStrbufAppendS(result, value);
return true;
}
if(argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY)
return false;
DBusMessageIter subIter;
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
if(argType == DBUS_TYPE_VARIANT)
return ffDBusGetValue(dbus, &subIter, result);
//At this point we have an array
bool foundAValue = false;
while(true)
{
if(ffDBusGetValue(dbus, &subIter, result))
{
foundAValue = true;
ffStrbufAppendS(result, ", ");
}
FF_DBUS_ITER_CONTINUE(dbus, &subIter);
}
if(foundAValue)
ffStrbufSubstrBefore(result, result->length - 2);
return foundAValue;
}
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result)
{
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
if(argType == DBUS_TYPE_BOOLEAN)
{
dbus_bool_t value = 0;
dbus->lib->ffdbus_message_iter_get_basic(iter, &value);
*result = value != 0;
return true;
}
if(argType != DBUS_TYPE_VARIANT)
return false;
DBusMessageIter subIter;
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
return ffDBusGetBool(dbus, &subIter, result);
}
bool ffDBusGetByte(FFDBusData* dbus, DBusMessageIter* iter, uint8_t* result)
{
int argType = dbus->lib->ffdbus_message_iter_get_arg_type(iter);
if(argType == DBUS_TYPE_BYTE)
{
dbus->lib->ffdbus_message_iter_get_basic(iter, result);
return true;
}
if(argType != DBUS_TYPE_VARIANT)
return false;
DBusMessageIter subIter;
dbus->lib->ffdbus_message_iter_recurse(iter, &subIter);
return ffDBusGetByte(dbus, &subIter, result);
}
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method)
{
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, interface, method);
if(message == NULL)
return NULL;
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, FF_DBUS_TIMEOUT_MILLISECONDS, NULL);
dbus->lib->ffdbus_message_unref(message);
return reply;
}
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property)
{
DBusMessage* message = dbus->lib->ffdbus_message_new_method_call(busName, objectPath, "org.freedesktop.DBus.Properties", "Get");
if(message == NULL)
return NULL;
DBusMessageIter requestIterator;
dbus->lib->ffdbus_message_iter_init_append(message, &requestIterator);
if(!dbus->lib->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &interface))
{
dbus->lib->ffdbus_message_unref(message);
return NULL;
}
if(!dbus->lib->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &property))
{
dbus->lib->ffdbus_message_unref(message);
return NULL;
}
DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, FF_DBUS_TIMEOUT_MILLISECONDS, NULL);
dbus->lib->ffdbus_message_unref(message);
return reply;
}
void ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result)
{
DBusMessage* reply = ffDBusGetProperty(dbus, busName, objectPath, interface, property);
if(reply == NULL)
return;
DBusMessageIter rootIterator;
if(!dbus->lib->ffdbus_message_iter_init(reply, &rootIterator))
{
dbus->lib->ffdbus_message_unref(reply);
return;
}
ffDBusGetValue(dbus, &rootIterator, result);
dbus->lib->ffdbus_message_unref(reply);
}

57
src/common/dbus.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#ifndef FF_INCLUDED_common_dbus
#define FF_INCLUDED_common_dbus
#ifdef FF_HAVE_DBUS
#include <dbus/dbus.h>
#include "util/FFstrbuf.h"
#include "common/library.h"
#define FF_DBUS_TIMEOUT_MILLISECONDS 35
#define FF_DBUS_ITER_CONTINUE(dbus, iterator) \
{ \
if(!(dbus)->lib->ffdbus_message_iter_has_next(iterator)) \
break; \
(dbus)->lib->ffdbus_message_iter_next(iterator); \
continue; \
}
typedef struct FFDBusLibrary
{
FF_LIBRARY_SYMBOL(dbus_bus_get)
FF_LIBRARY_SYMBOL(dbus_message_new_method_call)
FF_LIBRARY_SYMBOL(dbus_message_iter_init)
FF_LIBRARY_SYMBOL(dbus_message_iter_init_append)
FF_LIBRARY_SYMBOL(dbus_message_iter_append_basic)
FF_LIBRARY_SYMBOL(dbus_message_iter_get_arg_type)
FF_LIBRARY_SYMBOL(dbus_message_iter_get_basic)
FF_LIBRARY_SYMBOL(dbus_message_iter_recurse)
FF_LIBRARY_SYMBOL(dbus_message_iter_has_next)
FF_LIBRARY_SYMBOL(dbus_message_iter_next)
FF_LIBRARY_SYMBOL(dbus_message_unref)
FF_LIBRARY_SYMBOL(dbus_connection_send_with_reply_and_block)
FF_LIBRARY_SYMBOL(dbus_connection_flush)
FF_LIBRARY_SYMBOL(dbus_pending_call_block)
FF_LIBRARY_SYMBOL(dbus_pending_call_steal_reply)
FF_LIBRARY_SYMBOL(dbus_pending_call_unref)
} FFDBusLibrary;
typedef struct FFDBusData
{
const FFDBusLibrary* lib;
DBusConnection* connection;
} FFDBusData;
const char* ffDBusLoadData(const FFinstance* instance, DBusBusType busType, FFDBusData* data); //Returns an error message or NULL on success
bool ffDBusGetValue(FFDBusData* dbus, DBusMessageIter* iter, FFstrbuf* result);
bool ffDBusGetBool(FFDBusData* dbus, DBusMessageIter* iter, bool* result);
bool ffDBusGetByte(FFDBusData* dbus, DBusMessageIter* iter, uint8_t* result);
DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* method);
DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property);
void ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result);
#endif // FF_HAVE_DBUS
#endif // FF_INCLUDED_common_dbus

View File

@ -115,6 +115,7 @@ static void defaultConfig(FFinstance* instance)
initModuleArg(&instance->config.openGL);
initModuleArg(&instance->config.openCL);
initModuleArg(&instance->config.users);
initModuleArg(&instance->config.bluetooth);
ffStrbufInitA(&instance->config.libPCI, 0);
ffStrbufInitA(&instance->config.libVulkan, 0);
@ -377,6 +378,7 @@ static void destroyConfig(FFinstance* instance)
destroyModuleArg(&instance->config.openGL);
destroyModuleArg(&instance->config.openCL);
destroyModuleArg(&instance->config.users);
destroyModuleArg(&instance->config.bluetooth);
ffStrbufDestroy(&instance->config.libPCI);
ffStrbufDestroy(&instance->config.libVulkan);

View File

@ -295,6 +295,7 @@
#--opengl-key OpenGL
#--opencl-key OpenCL
#--users-key Users
#--bluetooth-key Bluetooth
# Format options:
# Sets the format string for module values.
@ -339,6 +340,7 @@
#--opengl-format
#--opencl-format
#--users-format
#--bluetooth-format
# Error options:
# Sets the format string to use if an error occured
@ -383,6 +385,7 @@
#--opengl-error
#--opencl-error
#--users-error
#--bluetooth-error
# Library options:
# Sets an user specific path to a library to load.

View File

@ -1,5 +1,6 @@
Battery
Bios
Bluetooth
Board
Break
Chassis

View File

@ -0,0 +1,21 @@
#include "bluetooth.h"
#include "../internal.h"
void ffDetectBluetoothImpl(const FFinstance* instance, FFBluetoothResult* bluetooth);
const FFBluetoothResult* ffDetectBluetooth(const FFinstance* instance)
{
FF_DETECTION_INTERNAL_GUARD(FFBluetoothResult,
ffStrbufInit(&result.error);
ffStrbufInit(&result.name);
ffStrbufInit(&result.address);
ffStrbufInit(&result.type);
result.battery = 0;
ffDetectBluetoothImpl(instance, &result);
if(result.error.length == 0 && result.name.length == 0)
ffStrbufAppendS(&result.error, "No bluetooth device found");
)
}

View File

@ -0,0 +1,19 @@
#pragma once
#ifndef FF_INCLUDED_detection_bluetooth_bluetooth
#define FF_INCLUDED_detection_bluetooth_bluetooth
#include "fastfetch.h"
typedef struct FFBluetoothResult
{
FFstrbuf error;
FFstrbuf name;
FFstrbuf address;
FFstrbuf type;
uint8_t battery; // 0-100%
} FFBluetoothResult;
const FFBluetoothResult* ffDetectBluetooth(const FFinstance* instance);
#endif

View File

@ -0,0 +1,200 @@
#include "bluetooth.h"
#ifdef FF_HAVE_DBUS
#include "common/dbus.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(
string "Address"
variant string "03:21:8B:91:16:4D"
)
dict entry(
string "Name"
variant string "JBL TUNE160BT"
)
dict entry(
string "Connected"
variant boolean true
)
]
)
dict entry( //property
string "org.bluez.Battery1"
array [
dict entry(
string "Percentage"
variant byte 100
)
]
)
]
)
]
*/
static void detectBluetoothProperty(FFBluetoothResult* bluetooth, FFDBusData* dbus, DBusMessageIter* iter, bool* connected)
{
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(strstr(propertyType, "Device") == NULL && strstr(propertyType, "Battery") == NULL)
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);
while(true)
{
if(dbus->lib->ffdbus_message_iter_get_arg_type(&arrayIter) != DBUS_TYPE_DICT_ENTRY)
FF_DBUS_ITER_CONTINUE(dbus, &arrayIter)
DBusMessageIter deviceIter;
dbus->lib->ffdbus_message_iter_recurse(&arrayIter, &deviceIter);
if(dbus->lib->ffdbus_message_iter_get_arg_type(&deviceIter) != DBUS_TYPE_STRING)
FF_DBUS_ITER_CONTINUE(dbus, &arrayIter)
const char* deviceProperty;
dbus->lib->ffdbus_message_iter_get_basic(&deviceIter, &deviceProperty);
dbus->lib->ffdbus_message_iter_next(&deviceIter);
if(strcmp(deviceProperty, "Address") == 0)
ffDBusGetValue(dbus, &deviceIter, &bluetooth->address);
else if(strcmp(deviceProperty, "Name") == 0)
ffDBusGetValue(dbus, &deviceIter, &bluetooth->name);
else if(strcmp(deviceProperty, "Icon") == 0)
ffDBusGetValue(dbus, &deviceIter, &bluetooth->type);
else if(strcmp(deviceProperty, "Percentage") == 0)
ffDBusGetByte(dbus, &deviceIter, &bluetooth->battery);
else if(strcmp(deviceProperty, "Connected") == 0)
{
ffDBusGetBool(dbus, &deviceIter, connected);
if(!*connected)
return; //Just for performance, we don't need to continue if we're not connected
}
FF_DBUS_ITER_CONTINUE(dbus, &arrayIter);
}
}
static bool detectBluetoothObject(FFBluetoothResult* bluetooth, FFDBusData* dbus, DBusMessageIter* iter)
{
if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY)
return false;
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 false;
const char* objectPath;
dbus->lib->ffdbus_message_iter_get_basic(&dictIter, &objectPath);
//We don't want adapter objects
if(strstr(objectPath, "dev_") == NULL)
return false;
dbus->lib->ffdbus_message_iter_next(&dictIter);
if(dbus->lib->ffdbus_message_iter_get_arg_type(&dictIter) != DBUS_TYPE_ARRAY)
return false;
DBusMessageIter arrayIter;
dbus->lib->ffdbus_message_iter_recurse(&dictIter, &arrayIter);
bool connected = false;
while(true)
{
detectBluetoothProperty(bluetooth, dbus, &arrayIter, &connected);
FF_DBUS_ITER_CONTINUE(dbus, &arrayIter);
}
if(!connected || bluetooth->name.length == 0)
{
ffStrbufClear(&bluetooth->address);
ffStrbufClear(&bluetooth->name);
ffStrbufClear(&bluetooth->type);
bluetooth->battery = 0;
return false;
}
return true;
}
static void detectBluetoothRoot(FFBluetoothResult* bluetooth, FFDBusData* dbus, DBusMessageIter* iter)
{
if(dbus->lib->ffdbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return;
DBusMessageIter arrayIter;
dbus->lib->ffdbus_message_iter_recurse(iter, &arrayIter);
while(true)
{
if(detectBluetoothObject(bluetooth, dbus, &arrayIter))
return;
FF_DBUS_ITER_CONTINUE(dbus, &arrayIter);
}
return;
}
static const char* detectBluetooth(const FFinstance* instance, FFBluetoothResult* bluetooth)
{
FFDBusData dbus;
const char* error = ffDBusLoadData(instance, DBUS_BUS_SYSTEM, &dbus);
if(error)
return error;
DBusMessage* managedObjects = ffDBusGetMethodReply(&dbus, "org.bluez", "/", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
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(bluetooth, &dbus, &rootIter);
dbus.lib->ffdbus_message_unref(managedObjects);
return NULL;
}
#endif
void ffDetectBluetoothImpl(const FFinstance* instance, FFBluetoothResult* bluetooth)
{
#ifdef FF_HAVE_DBUS
ffStrbufAppendS(&bluetooth->error, detectBluetooth(instance, bluetooth));
#else
ffStrbufAppendS(&bluetooth->error, "Fastfetch was compiled without DBus support");
#endif
}

View File

@ -8,208 +8,75 @@
#define FF_DBUS_TIMEOUT_MILLISECONDS 35
#ifdef FF_HAVE_DBUS
#include "common/dbus.h"
#include "common/library.h"
#include "common/parsing.h"
#include <dbus/dbus.h>
#define FF_DBUS_ITER_CONTINUE(iterator) \
{ \
if(!data->ffdbus_message_iter_has_next(&iterator)) \
break; \
data->ffdbus_message_iter_next(&iterator); \
continue; \
}
typedef struct DBusData
static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResult* result)
{
FF_LIBRARY_SYMBOL(dbus_message_new_method_call)
FF_LIBRARY_SYMBOL(dbus_message_iter_init)
FF_LIBRARY_SYMBOL(dbus_message_iter_init_append)
FF_LIBRARY_SYMBOL(dbus_message_iter_append_basic)
FF_LIBRARY_SYMBOL(dbus_message_iter_get_arg_type)
FF_LIBRARY_SYMBOL(dbus_message_iter_get_basic)
FF_LIBRARY_SYMBOL(dbus_message_iter_recurse)
FF_LIBRARY_SYMBOL(dbus_message_iter_has_next)
FF_LIBRARY_SYMBOL(dbus_message_iter_next)
FF_LIBRARY_SYMBOL(dbus_message_unref)
FF_LIBRARY_SYMBOL(dbus_connection_send_with_reply)
FF_LIBRARY_SYMBOL(dbus_connection_flush)
FF_LIBRARY_SYMBOL(dbus_pending_call_block)
FF_LIBRARY_SYMBOL(dbus_pending_call_steal_reply)
FF_LIBRARY_SYMBOL(dbus_pending_call_unref)
DBusConnection *connection;
} DBusData;
static bool getValue(DBusMessageIter* iter, FFstrbuf* result, DBusData* data)
{
int argType = data->ffdbus_message_iter_get_arg_type(iter);
if(argType == DBUS_TYPE_STRING)
{
const char* value = NULL;
data->ffdbus_message_iter_get_basic(iter, &value);
if(!ffStrSet(value))
return false;
ffStrbufAppendS(result, value);
return true;
}
if(argType != DBUS_TYPE_VARIANT && argType != DBUS_TYPE_ARRAY)
return false;
DBusMessageIter subIter;
data->ffdbus_message_iter_recurse(iter, &subIter);
if(argType == DBUS_TYPE_VARIANT)
return getValue(&subIter, result, data);
//At this point we have an array
bool foundAValue = false;
while(true)
{
if(getValue(&subIter, result, data))
{
foundAValue = true;
ffStrbufAppendS(result, ", ");
}
FF_DBUS_ITER_CONTINUE(subIter);
}
if(foundAValue)
ffStrbufSubstrBefore(result, result->length - 2);
return foundAValue;
}
static DBusMessage* getReply(DBusMessage* request, DBusData* data)
{
DBusPendingCall* pendingCall = NULL;
dbus_bool_t succesfull = data->ffdbus_connection_send_with_reply(data->connection, request, &pendingCall, FF_DBUS_TIMEOUT_MILLISECONDS);
data->ffdbus_message_unref(request);
if(!succesfull || pendingCall == NULL)
return NULL;
data->ffdbus_connection_flush(data->connection);
data->ffdbus_pending_call_block(pendingCall);
DBusMessage* reply = data->ffdbus_pending_call_steal_reply(pendingCall);
data->ffdbus_pending_call_unref(pendingCall);
return reply;
}
static DBusMessage* getProperty(const char* busName, const char* objectPath, const char* interface, const char* property, DBusData* data)
{
DBusMessage* message = data->ffdbus_message_new_method_call(busName, objectPath, "org.freedesktop.DBus.Properties", "Get");
if(message == NULL)
return NULL;
DBusMessageIter requestIterator;
data->ffdbus_message_iter_init_append(message, &requestIterator);
if(!data->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &interface))
{
data->ffdbus_message_unref(message);
return NULL;
}
if(!data->ffdbus_message_iter_append_basic(&requestIterator, DBUS_TYPE_STRING, &property))
{
data->ffdbus_message_unref(message);
return NULL;
}
return getReply(message, data);
}
static void getPropertyString(const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result, DBusData* data)
{
DBusMessage* reply = getProperty(busName, objectPath, interface, property, data);
if(reply == NULL)
return;
DBusMessageIter rootIterator;
if(!data->ffdbus_message_iter_init(reply, &rootIterator))
{
data->ffdbus_message_unref(reply);
return;
}
getValue(&rootIterator, result, data);
data->ffdbus_message_unref(reply);
}
static bool getBusProperties(const char* busName, FFMediaResult* result, DBusData* data)
{
DBusMessage* reply = getProperty(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "Metadata", data);
DBusMessage* reply = ffDBusGetProperty(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "Metadata");
if(reply == NULL)
return false;
DBusMessageIter rootIterator;
if(!data->ffdbus_message_iter_init(reply, &rootIterator))
if(!data->lib->ffdbus_message_iter_init(reply, &rootIterator))
{
data->ffdbus_message_unref(reply);
data->lib->ffdbus_message_unref(reply);
return false;
}
if(data->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_VARIANT)
if(data->lib->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_VARIANT)
{
data->ffdbus_message_unref(reply);
data->lib->ffdbus_message_unref(reply);
return false;
}
DBusMessageIter variantIterator;
data->ffdbus_message_iter_recurse(&rootIterator, &variantIterator);
if(data->ffdbus_message_iter_get_arg_type(&variantIterator) != DBUS_TYPE_ARRAY)
data->lib->ffdbus_message_iter_recurse(&rootIterator, &variantIterator);
if(data->lib->ffdbus_message_iter_get_arg_type(&variantIterator) != DBUS_TYPE_ARRAY)
{
data->ffdbus_message_unref(reply);
data->lib->ffdbus_message_unref(reply);
return false;
}
DBusMessageIter arrayIterator;
data->ffdbus_message_iter_recurse(&variantIterator, &arrayIterator);
data->lib->ffdbus_message_iter_recurse(&variantIterator, &arrayIterator);
while(true)
{
if(data->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY)
FF_DBUS_ITER_CONTINUE(arrayIterator)
if(data->lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY)
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
DBusMessageIter dictIterator;
data->ffdbus_message_iter_recurse(&arrayIterator, &dictIterator);
data->lib->ffdbus_message_iter_recurse(&arrayIterator, &dictIterator);
if(data->ffdbus_message_iter_get_arg_type(&dictIterator) != DBUS_TYPE_STRING)
FF_DBUS_ITER_CONTINUE(arrayIterator)
if(data->lib->ffdbus_message_iter_get_arg_type(&dictIterator) != DBUS_TYPE_STRING)
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
if(!data->ffdbus_message_iter_has_next(&dictIterator))
FF_DBUS_ITER_CONTINUE(arrayIterator)
if(!data->lib->ffdbus_message_iter_has_next(&dictIterator))
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
const char* key;
data->ffdbus_message_iter_get_basic(&dictIterator, &key);
data->lib->ffdbus_message_iter_get_basic(&dictIterator, &key);
data->ffdbus_message_iter_next(&dictIterator);
data->lib->ffdbus_message_iter_next(&dictIterator);
if(strcmp(key, "xesam:title") == 0)
getValue(&dictIterator, &result->song, data);
ffDBusGetValue(data, &dictIterator, &result->song);
else if(strcmp(key, "xesam:album") == 0)
getValue(&dictIterator, &result->album, data);
ffDBusGetValue(data, &dictIterator, &result->album);
else if(strcmp(key, "xesam:artist") == 0)
getValue(&dictIterator, &result->artist, data);
ffDBusGetValue(data, &dictIterator, &result->artist);
else if(strcmp(key, "xesam:url") == 0)
getValue(&dictIterator, &result->url, data);
ffDBusGetValue(data, &dictIterator, &result->url);
if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0 && result->url.length > 0)
break;
FF_DBUS_ITER_CONTINUE(arrayIterator)
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
}
data->ffdbus_message_unref(reply);
data->lib->ffdbus_message_unref(reply);
if(result->song.length == 0)
{
@ -223,20 +90,20 @@ static bool getBusProperties(const char* busName, FFMediaResult* result, DBusDat
ffStrbufAppendS(&result->busNameShort, busName + sizeof(FF_DBUS_MPRIS_PREFIX) - 1);
//We found a song, get the player name
getPropertyString(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "Identity", &result->player, data);
ffDBusGetPropertyString(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "Identity", &result->player);
if(result->player.length == 0)
getPropertyString(busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "DesktopEntry", &result->player, data);
ffDBusGetPropertyString(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "DesktopEntry", &result->player);
if(result->player.length == 0)
ffStrbufAppend(&result->player, &result->busNameShort);
return true;
}
static void getCustomBus(const FFinstance* instance, FFMediaResult* result, DBusData* data)
static void getCustomBus(FFDBusData* data, const FFinstance* instance, FFMediaResult* result)
{
if(ffStrbufStartsWithS(&instance->config.playerName, FF_DBUS_MPRIS_PREFIX))
{
getBusProperties(instance->config.playerName.chars, result, data);
getBusProperties(data, instance->config.playerName.chars, result);
return;
}
@ -244,88 +111,61 @@ static void getCustomBus(const FFinstance* instance, FFMediaResult* result, DBus
ffStrbufInit(&busName);
ffStrbufAppendS(&busName, FF_DBUS_MPRIS_PREFIX);
ffStrbufAppend(&busName, &instance->config.playerName);
getBusProperties(busName.chars, result, data);
getBusProperties(data, busName.chars, result);
ffStrbufDestroy(&busName);
}
static void getBestBus(FFMediaResult* result, DBusData* data)
static void getBestBus(FFDBusData* data, FFMediaResult* result)
{
if(
getBusProperties(FF_DBUS_MPRIS_PREFIX"spotify", result, data) ||
getBusProperties(FF_DBUS_MPRIS_PREFIX"vlc", result, data) ||
getBusProperties(FF_DBUS_MPRIS_PREFIX"plasma-browser-integration", result, data)
getBusProperties(data, FF_DBUS_MPRIS_PREFIX"spotify", result) ||
getBusProperties(data, FF_DBUS_MPRIS_PREFIX"vlc", result) ||
getBusProperties(data, FF_DBUS_MPRIS_PREFIX"plasma-browser-integration", result)
) return;
DBusMessage* message = data->ffdbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
if(message == NULL)
return;
DBusMessage* reply = getReply(message, data);
DBusMessage* reply = ffDBusGetMethodReply(data, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames");
if(reply == NULL)
return;
DBusMessageIter rootIterator;
if(!data->ffdbus_message_iter_init(reply, &rootIterator) || data->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_ARRAY)
if(!data->lib->ffdbus_message_iter_init(reply, &rootIterator) || data->lib->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_ARRAY)
return;
DBusMessageIter arrayIterator;
data->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator);
data->lib->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator);
while(true)
{
if(data->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING)
FF_DBUS_ITER_CONTINUE(arrayIterator)
if(data->lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING)
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
const char* busName;
data->ffdbus_message_iter_get_basic(&arrayIterator, &busName);
data->lib->ffdbus_message_iter_get_basic(&arrayIterator, &busName);
if(strncmp(busName, FF_DBUS_MPRIS_PREFIX, sizeof(FF_DBUS_MPRIS_PREFIX) - 1) != 0)
FF_DBUS_ITER_CONTINUE(arrayIterator)
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
if(getBusProperties(busName, result, data))
if(getBusProperties(data, busName, result))
break;
FF_DBUS_ITER_CONTINUE(arrayIterator)
FF_DBUS_ITER_CONTINUE(data, &arrayIterator)
}
data->ffdbus_message_unref(reply);
data->lib->ffdbus_message_unref(reply);
}
static const char* getMedia(const FFinstance* instance, FFMediaResult* result)
{
DBusData data;
FF_LIBRARY_LOAD(dbus, &instance->config.libDBus, "dlopen dbus failed", "libdbus-1" FF_LIBRARY_EXTENSION, 4);
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(dbus, dbus_bus_get)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_new_method_call)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_init)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_init_append)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_append_basic)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_get_arg_type)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_get_basic)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_recurse)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_has_next)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_iter_next)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_message_unref)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_connection_send_with_reply)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_connection_flush)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_pending_call_block)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_pending_call_steal_reply)
FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(dbus, data, dbus_pending_call_unref)
data.connection = ffdbus_bus_get(DBUS_BUS_SESSION, NULL);
if(data.connection == NULL)
{
dlclose(dbus);
return "Failed to connect to session dbus";
}
FFDBusData data;
const char* error = ffDBusLoadData(instance, DBUS_BUS_SESSION, &data);
if(error != NULL)
return error;
if(instance->config.playerName.length > 0)
getCustomBus(instance, result, &data);
getCustomBus(&data, instance, result);
else
getBestBus(result, &data);
getBestBus(&data, result);
dlclose(dbus);
return NULL;
}

View File

@ -448,6 +448,15 @@ static inline void printCommandHelp(const char* command)
"vendor"
);
}
else if(strcasecmp(command, "bluetooth-format") == 0)
{
constructAndPrintCommandHelpFormat("bluetooth", "{1} (4%)", 4,
"Name",
"Address",
"Type",
"Battery percentage"
);
}
else
fprintf(stderr, "No specific help for command %s provided\n", command);
}
@ -1208,6 +1217,7 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con
else if(optionParseModuleArgs(key, value, "opengl", &instance->config.openGL)) {}
else if(optionParseModuleArgs(key, value, "opencl", &instance->config.openCL)) {}
else if(optionParseModuleArgs(key, value, "users", &instance->config.users)) {}
else if(optionParseModuleArgs(key, value, "bluetooth", &instance->config.bluetooth)) {}
///////////////////
//Library options//
@ -1503,6 +1513,8 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char
ffPrintUsers(instance);
else if(strcasecmp(line, "command") == 0)
ffPrintCommand(instance);
else if(strcasecmp(line, "bluetooth") == 0)
ffPrintBluetooth(instance);
else
ffPrintErrorString(instance, line, 0, NULL, NULL, "<no implementation provided>");
}

View File

@ -139,6 +139,7 @@ typedef struct FFconfig
FFModuleArgs openGL;
FFModuleArgs openCL;
FFModuleArgs users;
FFModuleArgs bluetooth;
FFstrbuf libPCI;
FFstrbuf libVulkan;
@ -310,5 +311,6 @@ void ffPrintOpenGL(FFinstance* instance);
void ffPrintOpenCL(FFinstance* instance);
void ffPrintUsers(FFinstance* instance);
void ffPrintCommand(FFinstance* instance);
void ffPrintBluetooth(FFinstance* instance);
#endif

36
src/modules/bluetooth.c Normal file
View File

@ -0,0 +1,36 @@
#include "common/printing.h"
#include "detection/bluetooth/bluetooth.h"
#define FF_BLUETOOTH_MODULE_NAME "Bluetooth"
#define FF_BLUETOOTH_NUM_FORMAT_ARGS 4
void ffPrintBluetooth(FFinstance* instance)
{
const FFBluetoothResult* bluetooth = ffDetectBluetooth(instance);
if(bluetooth->error.length > 0)
{
ffPrintError(instance, FF_BLUETOOTH_MODULE_NAME, 0, &instance->config.bluetooth, "%s", bluetooth->error.chars);
return;
}
if(instance->config.bluetooth.outputFormat.length == 0)
{
ffPrintLogoAndKey(instance, FF_BLUETOOTH_MODULE_NAME, 0, &instance->config.bluetooth.key);
ffStrbufWriteTo(&bluetooth->name, stdout);
if(bluetooth->battery > 0)
printf(" (%d%%)", bluetooth->battery);
putchar('\n');
}
else
{
ffPrintFormat(instance, FF_BLUETOOTH_MODULE_NAME, 0, &instance->config.bluetooth, FF_BLUETOOTH_NUM_FORMAT_ARGS, (FFformatarg[]) {
{FF_FORMAT_ARG_TYPE_STRBUF, &bluetooth->name},
{FF_FORMAT_ARG_TYPE_STRBUF, &bluetooth->address},
{FF_FORMAT_ARG_TYPE_STRBUF, &bluetooth->type},
{FF_FORMAT_ARG_TYPE_UINT8, &bluetooth->battery}
});
}
}