Merge pull request #1540 from daangoossens22/bluetooth

feat: extend bluetooth module
This commit is contained in:
Alex 2022-05-11 09:26:38 +02:00 committed by GitHub
commit 3d023a0421
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 558 additions and 49 deletions

View File

@ -55,16 +55,12 @@
#include "modules/sndio.hpp" #include "modules/sndio.hpp"
#endif #endif
#ifdef HAVE_GIO_UNIX #ifdef HAVE_GIO_UNIX
#include "modules/bluetooth.hpp"
#include "modules/inhibitor.hpp" #include "modules/inhibitor.hpp"
#endif #endif
#include "bar.hpp" #include "bar.hpp"
#include "modules/custom.hpp" #include "modules/custom.hpp"
#include "modules/temperature.hpp" #include "modules/temperature.hpp"
#if defined(__linux__)
#ifdef WANT_RFKILL
#include "modules/bluetooth.hpp"
#endif
#endif
namespace waybar { namespace waybar {

View File

@ -1,18 +1,79 @@
#pragma once #pragma once
#include "ALabel.hpp" #include "ALabel.hpp"
#ifdef WANT_RFKILL
#include "util/rfkill.hpp" #include "util/rfkill.hpp"
#endif
#include <gio/gio.h>
#include <optional>
#include <string>
#include <vector>
namespace waybar::modules { namespace waybar::modules {
class Bluetooth : public ALabel { class Bluetooth : public ALabel {
struct ControllerInfo {
std::string path;
std::string address;
std::string address_type;
// std::string name; // just use alias instead
std::string alias;
bool powered;
bool discoverable;
bool pairable;
bool discovering;
};
// NOTE: there are some properties that not all devices provide
struct DeviceInfo {
std::string path;
std::string paired_controller;
std::string address;
std::string address_type;
// std::optional<std::string> name; // just use alias instead
std::string alias;
std::optional<std::string> icon;
bool paired;
bool trusted;
bool blocked;
bool connected;
bool services_resolved;
// NOTE: experimental feature in bluez
std::optional<unsigned char> battery_percentage;
};
public: public:
Bluetooth(const std::string&, const Json::Value&); Bluetooth(const std::string&, const Json::Value&);
~Bluetooth() = default; ~Bluetooth() = default;
auto update() -> void; auto update() -> void;
private: private:
static auto onInterfaceAddedOrRemoved(GDBusObjectManager*, GDBusObject*, GDBusInterface*,
gpointer) -> void;
static auto onInterfaceProxyPropertiesChanged(GDBusObjectManagerClient*, GDBusObjectProxy*,
GDBusProxy*, GVariant*, const gchar* const*,
gpointer) -> void;
auto getDeviceBatteryPercentage(GDBusObject*) -> std::optional<unsigned char>;
auto getDeviceProperties(GDBusObject*, DeviceInfo&) -> bool;
auto getControllerProperties(GDBusObject*, ControllerInfo&) -> bool;
auto findCurController(ControllerInfo&) -> bool;
auto findConnectedDevices(const std::string&, std::vector<DeviceInfo>&) -> void;
#ifdef WANT_RFKILL
util::Rfkill rfkill_; util::Rfkill rfkill_;
#endif
const std::unique_ptr<GDBusObjectManager, void (*)(GDBusObjectManager*)> manager_;
std::string state_;
ControllerInfo cur_controller_;
std::vector<DeviceInfo> connected_devices_;
DeviceInfo cur_focussed_device_;
std::string device_enumerate_;
std::vector<std::string> device_preference_;
}; };
} // namespace waybar::modules } // namespace waybar::modules

View File

@ -6,21 +6,41 @@ waybar - bluetooth module
# DESCRIPTION # DESCRIPTION
The *bluetooth* module displays information about the status of the device's bluetooth device. The *bluetooth* module displays information about a bluetooth controller and its connections.
# CONFIGURATION # CONFIGURATION
Addressed by *bluetooth* Addressed by *bluetooth*
*controller*: ++
typeof: string ++
Use the controller with the defined alias. Otherwise a random controller is used. Recommended to define when there is more than 1 controller available to the system.
*format-device-preference*: ++
typeof: array ++
A ranking of bluetooth devices, addressed by their alias. The order is from *first displayed* to *last displayed*. ++
If this config option is not defined or none of the devices in the list are connected, it will fall back to showing the last connected device.
*format*: ++ *format*: ++
typeof: string ++ typeof: string ++
default: *{icon}* ++ default: * {status}* ++
The format, how information should be displayed. This format is used when other formats aren't specified. The format, how information should be displayed. This format is used when other formats aren't specified.
*format-icons*: ++ *format-disabled*: ++
typeof: array/object ++ typeof: string ++
Based on the device status, the corresponding icon gets selected. ++ This format is used when the displayed controller is disabled.
The order is *low* to *high*. Or by the state if it is an object.
*format-off*: ++
typeof: string ++
This format is used when the displayed controller is turned off.
*format-on*: ++
typeof: string ++
This format is used when the displayed controller is turned on with no devices connected.
*format-connected*: ++
typeof: string ++
This format is used when the displayed controller is connected to at least 1 device.
*rotate*: ++ *rotate*: ++
typeof: integer ++ typeof: integer ++
@ -71,23 +91,94 @@ Addressed by *bluetooth*
typeof: string ++ typeof: string ++
The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified. The format, how information should be displayed in the tooltip. This format is used when other formats aren't specified.
*tooltip-format-disabled*: ++
typeof: string ++
This format is used when the displayed controller is disabled.
*tooltip-format-off*: ++
typeof: string ++
This format is used when the displayed controller is turned off.
*tooltip-format-on*: ++
typeof: string ++
This format is used when the displayed controller is turned on with no devices connected.
*tooltip-format-connected*: ++
typeof: string ++
This format is used when the displayed controller is connected to at least 1 device.
*tooltip-format-enumerate-connected*: ++
typeof: string ++
This format is used to define how each connected device should be displayed within the *device_enumerate* format replacement in the tooltip menu.
# FORMAT REPLACEMENTS # FORMAT REPLACEMENTS
*{status}*: Status of the bluetooth device. *{status}*: Status of the bluetooth device.
*{icon}*: Icon, as defined in *format-icons*. *{num_connections}*: Number of connections the displayed controller has.
*{controller_address}*: Address of the displayed controller.
*{controller_address_type}*: Address type of the displayed controller.
*{controller_alias}*: Alias of the displayed controller.
*{device_address}*: Address of the displayed device.
*{device_address_type}*: Address type of the displayed device.
*{device_alias}*: Alias of the displayed device.
*{device_enumerate}*: Show a list of all connected devices, each on a seperate line. Define the format of each device with the *tooltip-format-enumerate-connected* ++
and/or *tooltip-format-enumerate-connected-battery* config options. Can only be used in the tooltip related format options.
# EXPERIMENTAL BATTERY PERCENTAGE FEATURE
At the time of writing, the experimental features of BlueZ need to be turned on, for the battery percentage options listed below to work.
## FORMAT REPLACEMENT
*{device_battery_percentage}*: Battery percentage of the displayed device if available. Use only in the config options defined below.
## CONFIGURATION
*format-connected-battery*: ++
typeof: string ++
This format is used when the displayed device provides its battery percentage.
*tooltip-format-connected-battery*: ++
typeof: string ++
This format is used when the displayed device provides its battery percentage.
*tooltip-format-enumerate-connected-battery*: ++
typeof: string ++
This format is used to define how each connected device with a battery should be displayed within the *device_enumerate* format replacement option. ++
When this config option is not defined, it will fall back on the *tooltip-format-enumerate-connected* config option.
# EXAMPLES # EXAMPLES
``` ```
"bluetooth": { "bluetooth": {
"format": "{icon}", // "controller": "controller1", // specify the alias of the controller if there are more than 1 on the system
"format-alt": "bluetooth: {status}", "format": " {status}",
"format-icons": { "format-disabled": "", // an empty format will hide the module
"enabled": "", "format-connected": " {num_connections} connected",
"disabled": "" "tooltip-format": "{controller_alias}\\t{controller_address}",
}, "tooltip-format-connected": "{controller_alias}\\t{controller_address}\\n\\n{device_enumerate}",
"tooltip-format": "{}" "tooltip-format-enumerate-connected": "{device_alias}\\t{device_address}"
}
```
```
"bluetooth": {
"format": " {status}",
"format-connected": " {device_alias}",
"format-connected-battery": " {device_alias} {device_battery_percentage}%",
// "format-device-preference": [ "device1", "device2" ], // preference list deciding the displayed device
"tooltip-format": "{controller_alias}\\t{controller_address}\\n\\n{num_connections} connected",
"tooltip-format-connected": "{controller_alias}\\t{controller_address}\\n\\n{num_connections} connected\\n\\n{device_enumerate}",
"tooltip-format-enumerate-connected": "{device_alias}\\t{device_address}",
"tooltip-format-enumerate-connected-battery": "{device_alias}\\t{device_address}\\t{device_battery_percentage}%"
} }
``` ```
@ -95,3 +186,9 @@ Addressed by *bluetooth*
- *#bluetooth* - *#bluetooth*
- *#bluetooth.disabled* - *#bluetooth.disabled*
- *#bluetooth.off*
- *#bluetooth.on*
- *#bluetooth.connected*
- *#bluetooth.discoverable*
- *#bluetooth.discovering*
- *#bluetooth.pairable*

View File

@ -253,16 +253,14 @@ endif
if (giounix.found() and not get_option('logind').disabled()) if (giounix.found() and not get_option('logind').disabled())
add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp') add_project_arguments('-DHAVE_GIO_UNIX', language: 'cpp')
src_files += 'src/modules/inhibitor.cpp' src_files += 'src/modules/inhibitor.cpp'
src_files += 'src/modules/bluetooth.cpp'
endif endif
if get_option('rfkill').enabled() if get_option('rfkill').enabled() and is_linux
if is_linux
add_project_arguments('-DWANT_RFKILL', language: 'cpp') add_project_arguments('-DWANT_RFKILL', language: 'cpp')
src_files += files( src_files += files(
'src/modules/bluetooth.cpp',
'src/util/rfkill.cpp' 'src/util/rfkill.cpp'
) )
endif
endif endif
if tz_dep.found() if tz_dep.found()

View File

@ -101,6 +101,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
} }
#endif #endif
#ifdef HAVE_GIO_UNIX #ifdef HAVE_GIO_UNIX
if (ref == "bluetooth") {
return new waybar::modules::Bluetooth(id, config_[name]);
}
if (ref == "inhibitor") { if (ref == "inhibitor") {
return new waybar::modules::Inhibitor(id, bar_, config_[name]); return new waybar::modules::Inhibitor(id, bar_, config_[name]);
} }
@ -108,13 +111,6 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const {
if (ref == "temperature") { if (ref == "temperature") {
return new waybar::modules::Temperature(id, config_[name]); return new waybar::modules::Temperature(id, config_[name]);
} }
#if defined(__linux__)
#ifdef WANT_RFKILL
if (ref == "bluetooth") {
return new waybar::modules::Bluetooth(id, config_[name]);
}
#endif
#endif
if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) {
return new waybar::modules::Custom(ref.substr(7), id, config_[name]); return new waybar::modules::Custom(ref.substr(7), id, config_[name]);
} }

View File

@ -1,30 +1,391 @@
#include "modules/bluetooth.hpp" #include "modules/bluetooth.hpp"
#include <fmt/format.h> #include <fmt/format.h>
#include <spdlog/spdlog.h>
#include <algorithm>
#include <sstream>
namespace {
using GDBusManager = std::unique_ptr<GDBusObjectManager, void (*)(GDBusObjectManager*)>;
auto generateManager() -> GDBusManager {
GError* error = nullptr;
GDBusObjectManager* manager = g_dbus_object_manager_client_new_for_bus_sync(
G_BUS_TYPE_SYSTEM,
GDBusObjectManagerClientFlags::G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
"org.bluez", "/", NULL, NULL, NULL, NULL, &error);
if (error) {
spdlog::error("g_dbus_object_manager_client_new_for_bus_sync() failed: {}", error->message);
g_error_free(error);
}
auto destructor = [](GDBusObjectManager* manager) {
if (manager) {
g_object_unref(manager);
}
};
return GDBusManager{manager, destructor};
}
auto getBoolProperty(GDBusProxy* proxy, const char* property_name) -> bool {
auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name);
if (gvar) {
bool property_value = g_variant_get_boolean(gvar);
g_variant_unref(gvar);
return property_value;
}
spdlog::error("getBoolProperty() failed: doesn't have property {}", property_name);
return false;
}
auto getOptionalStringProperty(GDBusProxy* proxy, const char* property_name)
-> std::optional<std::string> {
auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name);
if (gvar) {
std::string property_value = g_variant_get_string(gvar, NULL);
g_variant_unref(gvar);
return property_value;
}
return std::nullopt;
}
auto getStringProperty(GDBusProxy* proxy, const char* property_name) -> std::string {
auto property_value = getOptionalStringProperty(proxy, property_name);
if (!property_value.has_value()) {
spdlog::error("getStringProperty() failed: doesn't have property {}", property_name);
}
return property_value.value_or("");
}
auto getUcharProperty(GDBusProxy* proxy, const char* property_name) -> unsigned char {
auto gvar = g_dbus_proxy_get_cached_property(proxy, property_name);
if (gvar) {
unsigned char property_value;
g_variant_get(gvar, "y", &property_value);
g_variant_unref(gvar);
return property_value;
}
spdlog::error("getUcharProperty() failed: doesn't have property {}", property_name);
return 0;
}
} // namespace
waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config) waybar::modules::Bluetooth::Bluetooth(const std::string& id, const Json::Value& config)
: ALabel(config, "bluetooth", id, "{icon}", 10), rfkill_{RFKILL_TYPE_BLUETOOTH} { : ALabel(config, "bluetooth", id, " {status}", 10),
#ifdef WANT_RFKILL
rfkill_{RFKILL_TYPE_BLUETOOTH},
#endif
manager_(generateManager()) {
if (config_["format-device-preference"].isArray()) {
std::transform(config_["format-device-preference"].begin(),
config_["format-device-preference"].end(),
std::back_inserter(device_preference_), [](auto x) { return x.asString(); });
}
// NOTE: assumption made that the controller that is selcected stays unchanged
// for duration of the module
if (!findCurController(cur_controller_)) {
if (config_["controller-alias"].isString()) {
spdlog::error("findCurController() failed: no bluetooth controller found with alias '{}'",
config_["controller-alias"].asString());
} else {
spdlog::error("findCurController() failed: no bluetooth controller found");
}
return;
}
findConnectedDevices(cur_controller_.path, connected_devices_);
g_signal_connect(manager_.get(), "interface-proxy-properties-changed",
G_CALLBACK(onInterfaceProxyPropertiesChanged), this);
g_signal_connect(manager_.get(), "interface-added", G_CALLBACK(onInterfaceAddedOrRemoved), this);
g_signal_connect(manager_.get(), "interface-removed", G_CALLBACK(onInterfaceAddedOrRemoved),
this);
#ifdef WANT_RFKILL
rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update))); rfkill_.on_update.connect(sigc::hide(sigc::mem_fun(*this, &Bluetooth::update)));
#endif
dp.emit();
} }
auto waybar::modules::Bluetooth::update() -> void { auto waybar::modules::Bluetooth::update() -> void {
std::string status = rfkill_.getState() ? "disabled" : "enabled"; // focussed device is either:
// - the first device in the device_preference_ list that is connected to the
label_.set_markup( // current controller (if none fallback to last connected device)
fmt::format(format_, fmt::arg("status", status), fmt::arg("icon", getIcon(0, status)))); // - it is the last device that connected to the current controller
if (status == "disabled") { if (!connected_devices_.empty()) {
label_.get_style_context()->add_class("disabled"); bool preferred_device_connected = false;
} else { if (!device_preference_.empty()) {
label_.get_style_context()->remove_class("disabled"); for (const std::string& device_alias : device_preference_) {
auto it =
std::find_if(connected_devices_.begin(), connected_devices_.end(),
[device_alias](auto device) { return device_alias == device.alias; });
if (it != connected_devices_.end()) {
preferred_device_connected = true;
cur_focussed_device_ = *it;
break;
}
}
}
if (!preferred_device_connected) {
cur_focussed_device_ = connected_devices_.back();
}
} }
if (tooltipEnabled()) { std::string state;
if (config_["tooltip-format"].isString()) { std::string tooltip_format;
auto tooltip_format = config_["tooltip-format"].asString(); if (!cur_controller_.powered)
auto tooltip_text = fmt::format(tooltip_format, status, fmt::arg("status", status)); state = "off";
label_.set_tooltip_text(tooltip_text); else if (!connected_devices_.empty())
state = "connected";
else
state = "on";
#ifdef WANT_RFKILL
if (rfkill_.getState()) state = "disabled";
#endif
if (!alt_) {
if (state == "connected" && cur_focussed_device_.battery_percentage.has_value() &&
config_["format-connected-battery"].isString()) {
format_ = config_["format-connected-battery"].asString();
} else if (config_["format-" + state].isString()) {
format_ = config_["format-" + state].asString();
} else if (config_["format"].isString()) {
format_ = config_["format"].asString();
} else { } else {
label_.set_tooltip_text(status); format_ = default_format_;
}
}
if (config_["tooltip-format-" + state].isString()) {
tooltip_format = config_["tooltip-format-" + state].asString();
} else if (config_["tooltip-format"].isString()) {
tooltip_format = config_["tooltip-format"].asString();
}
format_.empty() ? event_box_.hide() : event_box_.show();
auto update_style_context = [this](const std::string& style_class, bool in_next_state) {
if (in_next_state && !label_.get_style_context()->has_class(style_class)) {
label_.get_style_context()->add_class(style_class);
} else if (!in_next_state && label_.get_style_context()->has_class(style_class)) {
label_.get_style_context()->remove_class(style_class);
}
};
update_style_context("discoverable", cur_controller_.discoverable);
update_style_context("discovering", cur_controller_.discovering);
update_style_context("pairable", cur_controller_.pairable);
if (!state_.empty()) {
update_style_context(state_, false);
}
update_style_context(state, true);
state_ = state;
label_.set_markup(fmt::format(
format_, fmt::arg("status", state_), fmt::arg("num_connections", connected_devices_.size()),
fmt::arg("controller_address", cur_controller_.address),
fmt::arg("controller_address_type", cur_controller_.address_type),
fmt::arg("controller_alias", cur_controller_.alias),
fmt::arg("device_address", cur_focussed_device_.address),
fmt::arg("device_address_type", cur_focussed_device_.address_type),
fmt::arg("device_alias", cur_focussed_device_.alias),
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0))));
if (tooltipEnabled()) {
bool tooltip_enumerate_connections_ = config_["tooltip-format-enumerate-connected"].isString();
bool tooltip_enumerate_connections_battery_ =
config_["tooltip-format-enumerate-connected-battery"].isString();
if (tooltip_enumerate_connections_ || tooltip_enumerate_connections_battery_) {
std::stringstream ss;
for (DeviceInfo dev : connected_devices_) {
if ((tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value()) ||
tooltip_enumerate_connections_) {
ss << "\n";
std::string enumerate_format =
(tooltip_enumerate_connections_battery_ && dev.battery_percentage.has_value())
? config_["tooltip-format-enumerate-connected-battery"].asString()
: config_["tooltip-format-enumerate-connected"].asString();
ss << fmt::format(
enumerate_format, fmt::arg("device_address", dev.address),
fmt::arg("device_address_type", dev.address_type),
fmt::arg("device_alias", dev.alias),
fmt::arg("device_battery_percentage", dev.battery_percentage.value_or(0)));
}
}
device_enumerate_ = ss.str();
// don't start the connected devices text with a new line
if (!device_enumerate_.empty()) {
device_enumerate_.erase(0, 1);
}
}
label_.set_tooltip_text(fmt::format(
tooltip_format, fmt::arg("status", state_),
fmt::arg("num_connections", connected_devices_.size()),
fmt::arg("controller_address", cur_controller_.address),
fmt::arg("controller_address_type", cur_controller_.address_type),
fmt::arg("controller_alias", cur_controller_.alias),
fmt::arg("device_address", cur_focussed_device_.address),
fmt::arg("device_address_type", cur_focussed_device_.address_type),
fmt::arg("device_alias", cur_focussed_device_.alias),
fmt::arg("device_battery_percentage", cur_focussed_device_.battery_percentage.value_or(0)),
fmt::arg("device_enumerate", device_enumerate_)));
}
// Call parent update
ALabel::update();
}
// NOTE: only for when the org.bluez.Battery1 interface is added/removed after/before a device is
// connected/disconnected
auto waybar::modules::Bluetooth::onInterfaceAddedOrRemoved(GDBusObjectManager* manager,
GDBusObject* object,
GDBusInterface* interface,
gpointer user_data) -> void {
std::string interface_name = g_dbus_proxy_get_interface_name(G_DBUS_PROXY(interface));
std::string object_path = g_dbus_proxy_get_object_path(G_DBUS_PROXY(interface));
if (interface_name == "org.bluez.Battery1") {
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
auto device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(),
[object_path](auto d) { return d.path == object_path; });
if (device != bt->connected_devices_.end()) {
device->battery_percentage = bt->getDeviceBatteryPercentage(object);
bt->dp.emit();
} }
} }
} }
auto waybar::modules::Bluetooth::onInterfaceProxyPropertiesChanged(
GDBusObjectManagerClient* manager, GDBusObjectProxy* object_proxy, GDBusProxy* interface_proxy,
GVariant* changed_properties, const gchar* const* invalidated_properties, gpointer user_data)
-> void {
std::string interface_name = g_dbus_proxy_get_interface_name(interface_proxy);
std::string object_path = g_dbus_object_get_object_path(G_DBUS_OBJECT(object_proxy));
Bluetooth* bt = static_cast<Bluetooth*>(user_data);
if (interface_name == "org.bluez.Adapter1") {
if (object_path == bt->cur_controller_.path) {
bt->getControllerProperties(G_DBUS_OBJECT(object_proxy), bt->cur_controller_);
bt->dp.emit();
}
} else if (interface_name == "org.bluez.Device1" || interface_name == "org.bluez.Battery1") {
DeviceInfo device;
bt->getDeviceProperties(G_DBUS_OBJECT(object_proxy), device);
auto cur_device = std::find_if(bt->connected_devices_.begin(), bt->connected_devices_.end(),
[device](auto d) { return d.path == device.path; });
if (cur_device == bt->connected_devices_.end()) {
if (device.connected) {
bt->connected_devices_.push_back(device);
bt->dp.emit();
}
} else {
if (!device.connected) {
bt->connected_devices_.erase(cur_device);
} else {
*cur_device = device;
}
bt->dp.emit();
}
}
}
auto waybar::modules::Bluetooth::getDeviceBatteryPercentage(GDBusObject* object)
-> std::optional<unsigned char> {
GDBusProxy* proxy_device_bat =
G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Battery1"));
if (proxy_device_bat != NULL) {
unsigned char battery_percentage = getUcharProperty(proxy_device_bat, "Percentage");
g_object_unref(proxy_device_bat);
return battery_percentage;
}
return std::nullopt;
}
auto waybar::modules::Bluetooth::getDeviceProperties(GDBusObject* object, DeviceInfo& device_info)
-> bool {
GDBusProxy* proxy_device = G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Device1"));
if (proxy_device != NULL) {
device_info.path = g_dbus_object_get_object_path(object);
device_info.paired_controller = getStringProperty(proxy_device, "Adapter");
device_info.address = getStringProperty(proxy_device, "Address");
device_info.address_type = getStringProperty(proxy_device, "AddressType");
device_info.alias = getStringProperty(proxy_device, "Alias");
device_info.icon = getOptionalStringProperty(proxy_device, "Icon");
device_info.paired = getBoolProperty(proxy_device, "Paired");
device_info.trusted = getBoolProperty(proxy_device, "Trusted");
device_info.blocked = getBoolProperty(proxy_device, "Blocked");
device_info.connected = getBoolProperty(proxy_device, "Connected");
device_info.services_resolved = getBoolProperty(proxy_device, "ServicesResolved");
g_object_unref(proxy_device);
device_info.battery_percentage = getDeviceBatteryPercentage(object);
return true;
}
return false;
}
auto waybar::modules::Bluetooth::getControllerProperties(GDBusObject* object,
ControllerInfo& controller_info) -> bool {
GDBusProxy* proxy_controller =
G_DBUS_PROXY(g_dbus_object_get_interface(object, "org.bluez.Adapter1"));
if (proxy_controller != NULL) {
controller_info.path = g_dbus_object_get_object_path(object);
controller_info.address = getStringProperty(proxy_controller, "Address");
controller_info.address_type = getStringProperty(proxy_controller, "AddressType");
controller_info.alias = getStringProperty(proxy_controller, "Alias");
controller_info.powered = getBoolProperty(proxy_controller, "Powered");
controller_info.discoverable = getBoolProperty(proxy_controller, "Discoverable");
controller_info.pairable = getBoolProperty(proxy_controller, "Pairable");
controller_info.discovering = getBoolProperty(proxy_controller, "Discovering");
g_object_unref(proxy_controller);
return true;
}
return false;
}
auto waybar::modules::Bluetooth::findCurController(ControllerInfo& controller_info) -> bool {
bool found_controller = false;
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) {
GDBusObject* object = G_DBUS_OBJECT(l->data);
if (getControllerProperties(object, controller_info) &&
(!config_["controller-alias"].isString() ||
config_["controller-alias"].asString() == controller_info.alias)) {
found_controller = true;
break;
}
}
g_list_free_full(objects, g_object_unref);
return found_controller;
}
auto waybar::modules::Bluetooth::findConnectedDevices(const std::string& cur_controller_path,
std::vector<DeviceInfo>& connected_devices)
-> void {
GList* objects = g_dbus_object_manager_get_objects(manager_.get());
for (GList* l = objects; l != NULL; l = l->next) {
GDBusObject* object = G_DBUS_OBJECT(l->data);
DeviceInfo device;
if (getDeviceProperties(object, device) && device.connected &&
device.paired_controller == cur_controller_.path) {
connected_devices.push_back(device);
}
}
g_list_free_full(objects, g_object_unref);
}